欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > 标准的JNI (Java Native Interface) 加载函数 JNI_OnLoad

标准的JNI (Java Native Interface) 加载函数 JNI_OnLoad

2025/4/28 2:56:21 来源:https://blog.csdn.net/weixin_44737923/article/details/147313975  浏览:    关键词:标准的JNI (Java Native Interface) 加载函数 JNI_OnLoad

1.JNI_OnLoad

在 Android Native 开发中,JNI_OnLoad 是动态注册本地方法的标准入口点。以下是一个标准实现示例及其说明:


JNI_OnLoad 标准实现

#include <jni.h>
#include <string>// 声明本地方法对应的 C/C++ 函数
jint native_add(JNIEnv* env, jobject thiz, jint a, jint b) {return a + b;
}// 定义 JNINativeMethod 结构体数组
static JNINativeMethod gMethods[] = {// Java方法名 | 方法签名 | 本地函数指针{"add", "(II)I", (void*)native_add},
};// 缓存 JavaVM 实例(用于后续获取 JNIEnv)
JavaVM* gJavaVM = nullptr;JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {JNIEnv* env = nullptr;jint result = -1;// 1. 获取 JNIEnvif (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;}// 2. 缓存 JavaVM 实例gJavaVM = vm;// 3. 找到目标 Java 类const char* className = "com/example/MyJniClass";jclass clazz = env->FindClass(className);if (clazz == nullptr) {return JNI_ERR;}// 4. 注册本地方法if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0) {return JNI_ERR;}// 5. 返回使用的 JNI 版本(必须与获取 JNIEnv 时指定的版本一致)return JNI_VERSION_1_6;
}

关键步骤说明

  1. 获取 JNIEnv
    通过 JavaVM::GetEnv 获取 JNIEnv 指针,需指定 JNI 版本(通常为 JNI_VERSION_1_6)。

  2. 缓存 JavaVM
    JavaVM 实例保存到全局变量中,以便在其他线程中通过 AttachCurrentThread 获取 JNIEnv

  3. 查找 Java 类
    使用 FindClass 找到需要注册本地方法的 Java 类(需全限定类名,如 com/example/MyJniClass)。

  4. 注册本地方法
    调用 RegisterNativesJNINativeMethod 数组中的方法动态注册到目标类。每个 JNINativeMethod 包含:

    • Java 方法名:与 Java 代码中的 native 方法名一致。
    • 方法签名:描述方法的参数和返回值类型(可通过 javap -s <ClassName> 生成)。
    • 本地函数指针:C/C++ 实现的函数地址。
  5. 返回 JNI 版本
    需返回与 GetEnv 中指定的版本一致的 JNI 版本号(如 JNI_VERSION_1_6)。


注意事项

  1. 方法签名
    必须与 Java 方法的参数和返回值类型完全匹配。例如:

    • (II)I 表示接收两个 int 参数并返回 int
    • (Ljava/lang/String;)V 表示接收一个 String 参数且无返回值。
  2. 避免名称改编(C++)
    如果使用 C++,需通过 extern "C"#pragma GCC visibility 确保函数名称不被编译器改编:

    extern "C" JNIEXPORT jint JNICALL Java_com_example_MyJniClass_add(JNIEnv*, jobject, jint, jint);
    
  3. 错误处理
    检查 FindClassRegisterNatives 的返回值,避免因注册失败导致崩溃。

  4. 线程安全
    通过缓存的 JavaVM,可在其他线程中安全获取 JNIEnv

    JNIEnv* env;
    gJavaVM->AttachCurrentThread(&env, nullptr);
    

优势(动态注册 vs 静态注册)

  • 更高效:避免运行时通过方法名动态查找本地函数。
  • 更灵活:无需遵循 Java_<Package>_<Class>_<Method> 的命名格式。
  • 可控性:可在 JNI_OnLoad 中集中管理所有注册逻辑。

通过以上实现,可确保本地方法在 JNI 库加载时正确注册,提升代码可维护性和性能。

2.调用JNI_OnLoad

在 Android 开发中,JNI_OnLoad 不是由开发者直接调用的,而是由 Java 虚拟机(JVM)在加载动态库(.so 文件)时自动触发的。以下是它的调用机制和触发方式:


1. JNI_OnLoad 的调用机制

当你在 Java 代码中加载一个 JNI 动态库(.so 文件)时,系统会自动检查该库是否实现了 JNI_OnLoad 函数。如果存在,JVM 会在库加载后立即调用它。

触发 JNI_OnLoad 的方式

// Java 代码(例如 MainActivity.java)
static {System.loadLibrary("myjni"); // 加载 libmyjni.so
}
  • System.loadLibrary("myjni")
    会尝试加载 libmyjni.so(在 Android 中会自动添加 lib 前缀和 .so 后缀)。
  • JVM 检查 JNI_OnLoad
    如果 libmyjni.so 实现了 JNI_OnLoad,JVM 会在加载后立即调用它。

2. JNI_OnLoad 的调用流程

  1. Java 代码调用 System.loadLibrary("myjni")
  2. Android Runtime(ART/Dalvik)加载 libmyjni.so
  3. JVM 检查是否实现了 JNI_OnLoad
    • 如果存在,调用 JNI_OnLoad(JavaVM* vm, void* reserved)
    • 如果不存在,JNI 会使用默认的静态注册方式(基于 JNIEXPORT 函数名匹配)。
  4. JNI_OnLoad 执行动态注册
    • 注册 JNINativeMethod 数组中的本地方法。
    • 返回 JNI 版本(如 JNI_VERSION_1_6)。

3. 如何确保 JNI_OnLoad 被正确调用?

(1)确保 .so 文件正确编译

CMakeLists.txtAndroid.mk 中正确配置:

# CMakeLists.txt
add_library(myjni SHARED native-lib.cpp)

# Android.mk
LOCAL_MODULE := myjni
LOCAL_SRC_FILES := native-lib.cpp

(2)确保 Java 代码正确加载库

// 在静态代码块中加载库(避免重复加载)
static {System.loadLibrary("myjni");
}

(3)检查 JNI_OnLoad 是否被调用

可以在 JNI_OnLoad 中添加日志:

#include <android/log.h>
#define LOG_TAG "JNI_DEBUG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {LOGD("JNI_OnLoad called!"); // 检查是否执行// ... 其他注册逻辑return JNI_VERSION_1_6;
}

然后在 logcat 中查看日志:

adb logcat | grep "JNI_DEBUG"

4. 常见问题

(1)JNI_OnLoad 没有被调用?

  • 库未正确加载:检查 System.loadLibrary 是否成功执行(无 UnsatisfiedLinkError)。
  • .so 文件未正确编译:确保 JNI_OnLoad 被包含在最终 .so 中(可通过 nm -D libmyjni.so | grep JNI_OnLoad 检查)。
  • JNI 版本不匹配:确保 JNI_OnLoad 返回正确的版本(如 JNI_VERSION_1_6)。

(2)JNI_OnLoad 和静态注册的区别?

特性JNI_OnLoad(动态注册)静态注册(默认方式)
注册方式主动调用 RegisterNatives基于函数名匹配(如 Java_com_example_MyClass_method
性能更快(避免运行时查找)较慢(首次调用需解析)
灵活性可自定义注册逻辑必须遵循固定命名规则
适用场景推荐用于复杂项目简单 JNI 调用

5. 总结

  • JNI_OnLoad 由 JVM 自动调用,开发者只需实现它并在其中注册本地方法。
  • 触发方式:在 Java 中调用 System.loadLibrary("mylib")
  • 核心作用:动态注册 JNI 方法,提升性能并增强灵活性。
  • 调试技巧:通过 __android_log_print 打印日志,确保 JNI_OnLoad 被正确执行。

如果你的 JNI_OnLoad 没有被调用,请检查库加载流程和编译配置!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词