JNI的源码实现分析

💡 原文中文,约38500字,阅读约需92分钟。
📝

内容提要

在本文中,作者详细介绍了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。

🏷️

标签

➡️

继续阅读