JNI的源码实现分析
内容提要
在本文中,作者详细介绍了JNI的实现过程。首先,作者解释了在系统启动过程中,JNI的构建阶段,包括加载核心库、创建虚拟机对象和注册JNI函数。然后,作者讲解了在Java调用JNI函数时的运行时阶段,包括解析native方法、链接到汇编代码和调用注册的JNI函数。最后,作者介绍了在JNI调用Java函数时的过程,包括加载so库、调用JNI_OnLoad函数和通过函数信息调用Java函数。
延伸解读
JNI 映射建立的关键:RegisterNatives 与 ArtMethod
文章指出,Java 方法与 C++ 函数的映射关系并非自动产生,而是通过 JNI 的 RegisterNatives 接口显式建立。在 ART 中,该接口会遍历传入的 JNINativeMethod 数组,利用类、方法名和签名找到对应的 ArtMethod,再通过 ClassLinker::RegisterNative 将 C++ 函数指针设置到 ArtMethod 的 JNI 入口点。这意味着动态注册的本质是修改运行时方法结构中的函数指针,而非简单的名称绑定。
静态注册与动态注册的查找路径差异
文章对比了两种注册方式在调用时的不同处理。动态注册在首次调用前已通过 RegisterNatives 将函数指针写入 ArtMethod,因此 artFindNativeMethod 能直接返回已注册的 native_code。而静态注册没有提前注册,artFindNativeMethod 会转而调用 FindCodeForNativeMethod,依据 JNI 命名规则生成短名和长名,再通过 dlsym 在已加载的 so 库中查找符号。这解释了为何静态注册依赖固定命名格式。
从 zygote 启动看 JNI 环境的初始化链条
文章以 zygote 进程为例,展示了 JNI 环境在系统启动阶段的构建过程。AndroidRuntime::start 依次完成三件事:通过 JniInvocation 加载本地库并调用 JNI_CreateJavaVM 创建虚拟机;通过 startReg 动态注册系统预定义的 native 方法;最后通过 FindClass 和 CallStaticVoidMethod 调用 ZygoteInit.main 进入 Java 层。这一链条表明,Java 世界的运行依赖于 C++ 层提前搭建好的 JNI 基础设施。
JNI 调用 Java 时的类与方法查找机制
文章分析了 C++ 通过 JNIEnv 调用 Java 方法时的内部步骤。FindClass 会先在已加载的类表中查找,若未命中且类加载器为空,则从 boot class path 中查找;GetMethodID 则通过 FindMethodJNI 在目标类及其父类中按名称和签名扫描 ArtMethod。最终 CallVoidMethod 将 jmethodID 解码为 ArtMethod 指针并执行调用。这一过程说明,JNI 调用 Java 的效率受类查找和方法解析路径影响。
Q&A
JNI的主要功能是什么?
JNI是Java与C++之间的接口,主要用于实现两者的沟通。
JNI的实现过程分为哪几个阶段?
JNI的实现过程分为构建阶段、运行时Java调用JNI阶段和JNI调用Java阶段。
在Android中,JNI的接口声明在哪里?
在Android中,JNI的接口声明在libnativehelper.so动态库中,相关源码在jni.h中。
如何在Java中调用native方法?
在Java中调用native方法时,首先需要加载对应的so库,然后通过JNI提供的接口调用相应的native方法。
JNI中如何建立C++函数与Java方法的映射关系?
通过JNI的注册接口RegisterNatives来建立C++函数与Java方法的映射关系。
JNI的关键类型有哪些?
JNI的关键类型包括JavaVM、JNIEnv、jclass、jmethodID和jfieldID。