Java JNI 静态注册和动态注册
- 静态注册是指 Java native 方法和对应的本地函数按照预先定义好的命名规则来建立关联,实现了 Java native 方法与本地函数实现的绑定,这些函数会在 Java 类加载时被静态注册到 JVM 中,以确保 JVM 能够正确地找到对应的函数
- 动态注册是通过提供一个 Java native 方法和本地函数一一对应的映射表,然后在 JNI_OnLoad 回调里将映射表注册给 JVM,这样一来 JVM 就可以用这个映射表来调用相应的函数了,而不必像静态注册那样通过函数名去查找需要调用的函数,所以本地函数名不需要遵循特定命名规则
- PS:在 JNI 中静态注册和静态库之间没有直接的关系,无论是静态注册还是动态注册都可以生成动态库(.so文件)或者静态库(.a 文件)
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("myjni");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String str = stringFromJNI();
Log.e("TAG", "onCreate: str="+str);
}
public native String stringFromJNI();
}
静态注册
- 本地函数的函数名需要遵循 Java_类全限定名_方法名 的格式(类全限定名中的 . 需要用 _ 替换)命名规范
extern "C" JNIEXPORT jstring JNICALL
Java_com_louis_myjni_MainActivity_stringFromJNI(
JNIEnv* env,
jobject ) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
动态注册
- 当 Java 层调用 System.loadLibrary 方法时,JNI_OnLoad 函数会在库被加载到 Java 虚拟机时调用
- PS:比如 Android 系统的 JNI 函数和 FFmpeg 相关函数和都是使用的动态注册
jstring stringFromJNI_JNI_OnLoad(JNIEnv *env, jobject) {
std::string hello = "Hello from C++ stringFromJNI_JNI_OnLoad";
return env->NewStringUTF(hello.c_str());
}
JNINativeMethod nativeMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (jstring *) stringFromJNI_JNI_OnLoad}
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("获取 JNI 环境失败");
return JNI_ERR;
}
jclass clazz = env->FindClass("com/louis/myjni/MainActivity");
if (clazz == NULL) {
LOGE("获取 jclass 失败");
return JNI_ERR;
}
int nMethods = sizeof(nativeMethods) / sizeof(nativeMethods[0]);
if (env->RegisterNatives(clazz, nativeMethods, nMethods) != JNI_OK) {
LOGE("注册本地函数失败");
return JNI_ERR;
}
LOGE("注册成功");
return JNI_VERSION_1_6;
}
总结
- 静态注册简单易用,函数必须要有类似 JNIEXPORT jstring JNICALL 这样的相关声明,而且函数名必须要按照 JNI 的命名规则去命名,由于 JNI 函数名暴露,所以存在一定的安全隐患
- 动态注册灵活性高,不依赖于固定的命名规则,不过需要自行注册手动关联,可以做到在运行时根据条件选择不同的实现,更适合复杂项目