JNI绑定实践

背景

博主之前做的日志组件的结构是java和c++相结合实现。将日志的加密,压缩,文件写入等对性能比较敏感的模块放在c++层实现,而将日志的格式化,日志脱敏等功能放在java层实现。这就会涉及到java和c++的交互,即jni。
jni注册主要分为静态注册和动态注册,其中静态注册通过方法名进行绑定,方便调试,编辑器可以直接跳转,但是方法名比较冗长,不够灵活,适合接口不多的场景;而动态注册可以直接在代码里面进行绑定,实现更加灵活,但是不方便调试,编辑器不能直接跳转。

静态注册

1.引入so文件和native方法

首先定义一个java类,然后随便命名一个so库,如JniTest,然后定义两个native方法。

public class JniTestInterface {

    static {
        System.loadLibrary("JniTest");
    }

    private static JniTestInterface instance = new JniTestInterface();

    public static void set(String msg) {
        instance.jniSet(msg);
    }

    public static String get() {
        return instance.jniGet();
    }

    public native void jniSet(String msg);

    public native String jniGet();

}

2.编译生成jni头文件

在java文件夹下执行命令javac com/example/jnitest/JniTestInterface.java得到class文件
在这里插入图片描述
在这里插入图片描述

执行命令 javah com.example.jnitest.JniTestInterface 得到头文件
在这里插入图片描述
在这里插入图片描述

3.实现jni方法

在main文件夹下创建cpp文件夹,将头文件重命名为jniTest.h,并移动到cpp文件夹下
在这里插入图片描述
接着新建jniTest.cpp文件,并实现头文件当中的方法

#include "jniTest.h"
#include <stdio.h>

extern "C" JNIEXPORT void JNICALL Java_com_example_jnitest_JniTestInterface_jniSet
  (JNIEnv * env, jobject, jstring string){
      printf("调用了setStr方法哟!");
      char* str = (char *)env->GetStringUTFChars(string,NULL);
      printf("%s\n",str);
      env->ReleaseStringUTFChars(string,str);
  }


extern "C" JNIEXPORT jstring JNICALL Java_com_example_jnitest_JniTestInterface_jniGet
  (JNIEnv * env, jobject){
      printf("调用了getStr方法哟!");
      return env->NewStringUTF("hello from JNI !");
  }

4.实现CMakeLists.txt

编辑CMakeLists.txt文件

# 项目信息
project(JniHelper)
cmake_minimum_required(VERSION 3.4.1)

# 源文件
set(
        SRC_FILES
        ${CMAKE_SOURCE_DIR}/jniTest.h
        ${CMAKE_SOURCE_DIR}/jniTest.cpp
)

# 加入到动态库当中
add_library(
        JniTest
        SHARED
        ${SRC_FILES}
)

编辑build.gradle文件

android {
   
    ...

    defaultConfig {

        ...

        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
        }

        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a' // <- only the supported ones
        }
    }


    ...

    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.6.0"
        }
    }

}

动态注册

我们可以看到,静态注册通过方法名来关联java和c++的代码,c++的方法名会非常的长,当项目比较大,接口比较多时,就不方便进行管理,这时可以通过动态注册来解决。
接着来看下动态注册是怎么做的吧

1.定义对应交互的java方法

我们将新的动态库命名为JniDynamicTest

public class JniInterface {

    static {
        System.loadLibrary("JniDynamicTest");
    }

    private static JniInterface instance = new JniInterface();

    public static void set(String msg) {
        instance.jniDynamicSet(msg);
    }

    public static String get() {
        return instance.jniDynamicGet();
    }

    public native void jniDynamicSet(String msg);

    public native String jniDynamicGet();
}

2.引入c++代码和CMakeLists.txt文件

在main文件夹下新建cpp目录,并新建jniTest.cpp和CMakeLists.txt文件
在这里插入图片描述

3.编辑c++文件

c++的逻辑比较简单,主要分成以下几个步骤

1.定义方法映射表,完成java方法和c++方法的映射关系

2.注册所有的方法

3.jni_onload时,加载所有的注册方法

#include <jni.h>
#include <string>

#ifndef SIZEOF
#define SIZEOF(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif

extern "C" void jniSet(JNIEnv *env, jobject, jstring string) {
    printf("调用了setStr方法哟!");
    char *str = (char *) env->GetStringUTFChars(string, NULL);
    printf("%s\n", str);
    env->ReleaseStringUTFChars(string, str);
}

extern "C" jstring jniGet(JNIEnv *env, jobject) {
    printf("调用了getStr方法哟!");
    return env->NewStringUTF("hello Dynamic from JNI !");
}

/**
 * 1.定义方法映射表
 */
static const JNINativeMethod jniMethods[] = {
        {"jniDynamicSet", "(Ljava/lang/String;)V", (void *) jniSet},
        {"jniDynamicGet", "()Ljava/lang/String;",  (void *) jniGet}
};

/**
 * 2.注册所有方法
 */
static int registerAllMethods(JNIEnv *env) {
    const char *className = "com/example/jnidynamic/JniInterface";//对应的java类
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, jniMethods, SIZEOF(jniMethods)) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/**
 * 3.jni_onload时,加载所有的注册方法
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    if (!registerAllMethods(env)) {//注册所有方法
        return -1;
    }
    return JNI_VERSION_1_4;
}

4.实现CMakeLists.txt

和静态绑定的CMakeLists.txt大同小异

# 项目信息
project(JniHelper)
cmake_minimum_required(VERSION 3.4.1)

# 源文件
set(
        SRC_FILES
        ${CMAKE_SOURCE_DIR}/jniTest.cpp
)

# 加入到动态库当中
add_library(
        JniDynamicTest
        SHARED
        ${SRC_FILES}
)

build.gradle文件同静态绑定

参考:https://www.jianshu.com/p/54da9c2c3403

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值