概述
jni简单的说就是java调用c,注册c的方法有两种,一种是静态注册,一种是动态注册。静态注册就是通过javah生成.h文件来实现,不是本文重点。动态注册通过JNI_OnLoad来注册native方法,下面直接上代码。
java层
public class FileUtils {
static {
System.loadLibrary("native-lib");
}
public static native void diff(String path, String pattern_Path, int file_num);
}
直接新建一个类来管理native方法,其他activity的代码就不贴了,直接调用即可。
native层
调用System.loadLibrary(“native-lib”);会进入JNI_OnLoad,通过这个方法来注册。
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4)) {
return -1;
}
assert(env != NULL);
registerNatives(env);
return JNI_VERSION_1_4;
}
这个方法主要是通过JavaVM 去获取JNIEnv ,有了这个指针就可以注册native方法了,在看registerNatives方法之前需要做一些准备工作。
JNIEXPORT void JNICALL native_diff
(JNIEnv *, jclass, jstring, jstring, jint) {
LOGI("%s\n", "注册成功");
}
static const JNINativeMethod gMethods[] = {
{
"diff", "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_diff
}
};
首先声明一个方法,这个方法的名字可以任意指定,这个就是native注册的方法,接着声明一个结构体数组
gMethods[],里头包含了注册的对应关系。JNINativeMethod 的声明
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
name是java层的方法名,signature是对应的方法指针,fnPtr是c层的函数指针,做一一对应关系。
最后看看registerNatives方法。
int registerMainNative(JNIEnv *env) {
LOGI("register main start");
jclass clz = env->FindClass("com/example/ty/sourceproject/MainActivity");
if (NULL == clz) {
LOGI("mainclass is null");
return JNI_FALSE;
}
if (env->RegisterNatives(clz, mainMethod, NELEM(mainMethod)) < 0) {
LOGI("registernatives error");
return JNI_FALSE;
}
return JNI_TRUE;
}
方法很简单就是通过env来注册多个方法,NELME和LOGI的宏定义是这样的
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
工作作完,剩下的直接调用逻辑即可。
总结
动态注册主要就是3步:
1.通过JNI_OnLoad获取env指针。
2.编写native方法以及对应的结构体数组
3.注册方法
关于动态注册和静态注册的优缺点:
静态注册:
优点:每个class都需要使用javah生成一个头文件,并且生成的名字很长书写不便;初次调用时需要依据名字搜索对应的JNI层函数来建立关联关系,会影响运行效率
缺点:用javah 生成头文件方便简单
动态注册:
优点:使用一种数据结构JNINativeMethod来记录java native函数和JNI函数的对应关系
移植方便(一个java文件中有多个native方法,java文件的包名更换后)
缺点:使用麻烦