JNI的本质解析:Android Framework视角下的Java-Native衔接机制
基于Android 14源代码分析
目录
1. 概述
1.1 什么是JNI?
JNI (Java Native Interface) 是Java平台提供的一套标准API,用于实现Java代码与本地C/C++代码之间的互操作。在Android系统中,JNI扮演着至关重要的角色,它是Java层Framework与底层Native代码的桥梁。
1.2 为什么Android需要JNI?
Android系统架构采用分层设计:
- 应用层和Framework层:使用Java/Kotlin编写,提供高级API和业务逻辑
- Native层:使用C/C++编写,提供底层系统调用、硬件访问和性能关键路径
JNI在其中承担的作用包括:
- 性能优化:将计算密集型任务交给Native代码执行
- 系统调用:访问Linux内核提供的系统调用
- 硬件抽象:通过HAL(Hardware Abstraction Layer)访问硬件设备
- 代码复用:复用已有的C/C++库
1.3 本文研究范围
本文基于Android 14 (AOSP) 源代码,深入分析以下内容:
- JNI的注册机制
- Java与Native之间的调用链路
- 核心数据结构的实现
- 实际案例分析
2. JNI架构设计
2.1 整体架构
┌──────────────────────────────────────┐
│ Java Application Layer │
│ (Java/Kotlin Code) │
└──────────────┬───────────────────────┘
│ native method call
▼
┌──────────────────────────────────────┐
│ JNI Interface Layer │
│ - JNIEnv (per-thread) │
│ - JavaVM (per-process) │
│ - Method Mapping │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Native Implementation │
│ (C/C++ Code in .so libraries) │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ System Libraries / HAL │
└──────────────────────────────────────┘
2.2 关键组件
在Android Framework中,JNI的核心组件位于以下位置:
| 组件 | 源码路径 | 作用 |
|---|---|---|
| AndroidRuntime | frameworks/base/core/jni/AndroidRuntime.cpp |
JNI运行时初始化和注册管理 |
| JNI实现 | frameworks/base/core/jni/*.cpp |
Framework各模块的JNI实现 |
| JNIHelp | libnativehelper/ |
JNI辅助工具库 |
3. JNI注册机制:静态与动态的双轨制
JNI方法注册是Java方法与Native函数建立映射关系的过程。Android支持两种注册方式。
3.1 静态注册(隐式注册)
3.1.1 原理
静态注册依赖命名规则,虚拟机在首次调用native方法时,根据固定的命名格式查找对应的Native函数。
命名格式:
Java_<package_name>_<class_name>_<method_name>
例如:
- Java方法:
com.example.MyClass.nativeMethod() - 对应Native函数:
Java_com_example_MyClass_nativeMethod()
3.1.2 优缺点
优点:
- 实现简单,无需手动注册代码
- 适合小型项目和简单场景
缺点:
- 函数名冗长,可读性差
- 首次调用时需要符号查找,性能较低
- Android Framework中很少使用
3.2 动态注册(显式注册)
3.2.1 核心机制
Android Framework几乎全部采用动态注册方式。这种方式通过RegisterNatives()函数显式建立映射关系。
3.2.2 源码分析:AndroidRuntime的注册流程
第一步:定义注册函数表
在frameworks/base/core/jni/AndroidRuntime.cpp中,定义了所有需要注册的模块:
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:1472-1477
#ifdef NDEBUG
#define REG_JNI(name) { name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
#else
#define REG_JNI(name) { name, #name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
const char* mName;
};
#endif
第二步:注册表数组
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:1499-1527
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_os_Binder),
REG_JNI(register_android_os_Parcel),
REG_JNI(register_android_os_MessageQueue),
// ... 数百个注册项
};
第三步:统一注册入口
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:1669-1698
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
ATRACE_NAME("RegisterAndroidNatives");
/*
* 设置线程创建钩子,使所有新线程自动附加到JavaVM
*/
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
ALOGV("--- registering native functions ---\n");
/*
* 使用PushLocalFrame管理本地引用
* 注册过程会创建大量本地引用(如FindClass),需要及时释放
*/
env->PushLocalFrame(200);
// 批量注册所有JNI方法
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
return 0;
}
第四步:调用时机
在AndroidRuntime启动虚拟机时调用:
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:1236-1251
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
3.3 具体模块的注册实现
以MessageQueue为例,展示完整的注册过程:
3.3.1 定义JNI方法表
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:246-255
static const JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
{ "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
{ "nativeSetFileDescriptorEvents", "(JII)V",
(void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
};
方法表结构说明:
name:Java层native方法名signature:JNI方法签名(参数和返回值类型)funcPtr:对应的C++函数指针
JNI签名规则:
| Java类型 | JNI签名 |
|---|---|
| void | V |
| boolean | Z |
| int | I |
| long | J |
| String | Ljava/lang/String; |
| int[] | [I |
3.3.2 注册函数实现
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:257-267
int register_android_os_MessageQueue(JNIEnv* env) {
// 注册native方法
int res = RegisterMethodsOrDie(env, "android/os/MessageQueue",
gMessageQueueMethods,
NELEM(gMessageQueueMethods));
// 缓存Java类的字段和方法ID(性能优化)
jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");
gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,
"dispatchEvents", "(II)I");
return res;
}
关键点分析:
- RegisterMethodsOrDie:批量注册方法,失败则终止进程
- 缓存字段和方法ID:避免运行时频繁查找,提升性能
- 全局结构体:将ID存储在静态结构体中
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:30-33
static struct {
jfieldID mPtr; // native object attached to the DVM MessageQueue
jmethodID dispatchEvents;
} gMessageQueueClassInfo;
4. 核心数据结构:JNIEnv与JavaVM
4.1 JavaVM:进程级虚拟机实例
4.1.1 定义与作用
JavaVM代表整个Java虚拟机实例,每个进程只有一个JavaVM对象。
主要功能:
- 创建和销毁虚拟机
- 附加/分离线程
- 获取JNIEnv指针
4.1.2 源码中的使用
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:299
/*static*/ JavaVM* AndroidRuntime::mJavaVM = NULL;
// 获取JavaVM实例
/*static*/ JavaVM* AndroidRuntime::getJavaVM() {
return AndroidRuntime::mJavaVM;
}
// frameworks/base/core/jni/AndroidRuntime.cpp:1329-1331
extern "C" JavaVM* AndroidRuntimeGetJavaVM() {
return AndroidRuntime::getJavaVM();
}
4.1.3 线程附加机制
Native线程需要附加到JavaVM才能调用Java代码:
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:1338-1347
/*static*/ JNIEnv* AndroidRuntime::getJNIEnv()
{
JNIEnv* env;
JavaVM* vm = AndroidRuntime::getJavaVM();
assert(vm != NULL);
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
return NULL; // 线程未附加
return env;
}
4.2 JNIEnv:线程级接口指针
4.2.1 本质
JNIEnv是一个指向函数表的指针,每个线程都有独立的JNIEnv实例。
为什么是线程独立的?
- 避免多线程竞争
- 支持线程本地异常处理
- 简化JNI实现
4.2.2 常用API分类
1. 类和对象操作
cpp
jclass FindClass(const char* name);
jobject NewObject(jclass clazz, jmethodID methodID, ...);
jclass GetObjectClass(jobject obj);
2. 字段访问
cpp
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
jint GetIntField(jobject obj, jfieldID fieldID);
void SetIntField(jobject obj, jfieldID fieldID, jint value);
3. 方法调用
cpp
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig);
void CallVoidMethod(jobject obj, jmethodID methodID, ...);
jint CallIntMethod(jobject obj, jmethodID methodID, ...);
4. 异常处理
cpp
jboolean ExceptionCheck();
jthrowable ExceptionOccurred();
void ExceptionClear();
void Throw(jthrowable obj);
5. 引用管理
cpp
jobject NewLocalRef(jobject ref);
void DeleteLocalRef(jobject localRef);
jobject NewGlobalRef(jobject obj);
void DeleteGlobalRef(jobject globalRef);
5. 完整调用链路剖析
5.1 Java调用Native:从上到下
流程图
Java Application
│
│ 1. 调用native方法
▼
MessageQueue.nativePollOnce(ptr, timeout)
│
│ 2. ART查找native函数地址(已注册)
▼
android_os_MessageQueue_nativePollOnce()
│
│ 3. 解析参数
▼
NativeMessageQueue::pollOnce(env, obj, timeoutMillis)
│
│ 4. 调用Native逻辑
▼
Looper::pollOnce(timeoutMillis)
│
│ 5. 系统调用
▼
epoll_wait() [Linux kernel]
详细步骤
Step 1: Java层声明native方法
java
// frameworks/base/core/java/android/os/MessageQueue.java:69-73
private long mPtr; // used by native code
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
Step 2: JNI层实现
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:222-226
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
关键点:
jlong ptr:从Java层传递的Native对象指针(转换为long)reinterpret_cast:将long指针还原为C++对象指针
Step 3: Native对象实现
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:122-134
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis); // 调用底层Looper
mPollObj = NULL;
mPollEnv = NULL;
// 处理异常
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
5.2 Native回调Java:从下到上
场景示例
当Native层检测到文件描述符事件时,需要回调Java层的dispatchEvents方法。
实现代码
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:157-178
int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) {
int events = 0;
if (looperEvents & Looper::EVENT_INPUT) {
events |= CALLBACK_EVENT_INPUT;
}
if (looperEvents & Looper::EVENT_OUTPUT) {
events |= CALLBACK_EVENT_OUTPUT;
}
if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) {
events |= CALLBACK_EVENT_ERROR;
}
int oldWatchedEvents = reinterpret_cast<intptr_t>(data);
// 通过JNIEnv调用Java方法
int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj,
gMessageQueueClassInfo.dispatchEvents, fd, events);
if (!newWatchedEvents) {
return 0; // unregister the fd
}
if (newWatchedEvents != oldWatchedEvents) {
setFileDescriptorEvents(fd, newWatchedEvents);
}
return 1;
}
关键技术点:
- 保存JNIEnv :在
pollOnce时保存,回调时使用 - 使用缓存的methodID :
gMessageQueueClassInfo.dispatchEvents - 类型转换:JNI类型与Java类型的映射
6. 实战案例:MessageQueue的JNI实现
6.1 架构设计
MessageQueue是Android消息机制的核心组件,其JNI设计是典型的Java-Native协作模式。
设计思想
Java层MessageQueue
├─ 提供高级API(enqueueMessage, next等)
├─ 持有Native对象指针(mPtr)
└─ 委托Native层执行阻塞操作
Native层NativeMessageQueue
├─ 持有Looper对象
├─ 封装epoll机制
└─ 处理文件描述符事件
6.2 对象生命周期管理
创建流程
Java层:
java
// frameworks/base/core/java/android/os/MessageQueue.java:79
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit(); // 创建Native对象
}
Native层:
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:206-215
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env); // 增加引用计数
return reinterpret_cast<jlong>(nativeMessageQueue); // 返回指针作为long
}
NativeMessageQueue构造函数:
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:93-100
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
销毁流程
Java层:
java
// frameworks/base/core/java/android/os/MessageQueue.java:95
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
Native层:
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:217-220
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env); // 减少引用计数,可能触发析构
}
6.3 线程安全设计
环境保存机制
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:69-73
private:
JNIEnv* mPollEnv; // 当前轮询的线程环境
jobject mPollObj; // Java层MessageQueue对象
jthrowable mExceptionObj; // 待抛出的异常
为什么需要保存?
pollOnce可能触发回调,回调需要访问Java对象- 异常需要在正确的线程环境中抛出
6.4 异常处理机制
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:82-91
bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) {
if (env->ExceptionCheck()) {
jthrowable exceptionObj = env->ExceptionOccurred();
env->ExceptionClear();
raiseException(env, msg, exceptionObj);
env->DeleteLocalRef(exceptionObj);
return true;
}
return false;
}
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:105-120
void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) {
if (exceptionObj) {
if (mPollEnv == env) {
// 在轮询线程中,保存异常稍后抛出
if (mExceptionObj) {
env->DeleteLocalRef(mExceptionObj);
}
mExceptionObj = jthrowable(env->NewLocalRef(exceptionObj));
ALOGE("Exception in MessageQueue callback: %s", msg);
jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
} else {
// 不在轮询线程,直接致命错误
ALOGE("Exception: %s", msg);
jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
LOG_ALWAYS_FATAL("raiseException() was called when not in a callback, exiting.");
}
}
}
7. 性能优化与最佳实践
7.1 缓存字段和方法ID
原理
字段和方法ID查找是昂贵的操作,应该在注册时缓存。
实践
cpp
// frameworks/base/core/jni/android_os_MessageQueue.cpp:30-33
static struct {
jfieldID mPtr;
jmethodID dispatchEvents;
} gMessageQueueClassInfo;
// 注册时初始化
int register_android_os_MessageQueue(JNIEnv* env) {
// ...
jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");
gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,
"dispatchEvents", "(II)I");
return res;
}
性能对比:
- 每次调用查找:
FindClass + GetMethodID≈ 1000ns - 使用缓存:直接访问 ≈ 10ns
7.2 本地引用管理
问题
JNI调用创建的本地引用(Local Reference)需要手动释放,否则会导致内存泄漏。
解决方案
方案1:手动释放
cpp
jstring str = env->NewStringUTF("test");
// 使用str...
env->DeleteLocalRef(str);
方案2:使用PushLocalFrame/PopLocalFrame
cpp
env->PushLocalFrame(100); // 预分配100个引用槽位
// 创建大量本地引用...
env->PopLocalFrame(NULL); // 批量释放
AndroidRuntime中的实践:
cpp
// frameworks/base/core/jni/AndroidRuntime.cpp:1687-1693
env->PushLocalFrame(200);
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
7.3 全局引用的正确使用
场景
Native层需要长期持有Java对象时,必须使用全局引用。
示例
cpp
class MyNativeClass {
private:
jobject mJavaCallback; // 错误:应该用全局引用
public:
void setCallback(JNIEnv* env, jobject callback) {
// 正确做法
if (mJavaCallback) {
env->DeleteGlobalRef(mJavaCallback);
}
mJavaCallback = env->NewGlobalRef(callback);
}
~MyNativeClass() {
// 必须在析构时释放
if (mJavaCallback) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mJavaCallback);
}
}
};
7.4 减少JNI边界跨越
原则
JNI调用有固定开销(约20-200ns),应该批量处理数据。
反例
cpp
// 每次传递一个元素(性能差)
for (int i = 0; i < 10000; i++) {
env->CallVoidMethod(obj, processMethod, data[i]);
}
正例
cpp
// 批量传递数组
jintArray arr = env->NewIntArray(10000);
env->SetIntArrayRegion(arr, 0, 10000, data);
env->CallVoidMethod(obj, processBatchMethod, arr);
env->DeleteLocalRef(arr);
7.5 异常检查
规则
调用可能抛出异常的JNI函数后,必须检查异常。
实践
cpp
jclass clazz = env->FindClass("com/example/MyClass");
if (env->ExceptionCheck()) {
env->ExceptionClear();
// 处理异常...
return -1;
}
AndroidRuntime的封装:
cpp
// 使用OrDie系列函数,异常时直接终止进程
jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
jfieldID field = GetFieldIDOrDie(env, clazz, "mPtr", "J");
8. 总结
8.1 核心要点回顾
-
双轨注册机制
- 静态注册:命名规则,自动查找
- 动态注册:显式映射,性能更优(Android Framework主流)
-
核心数据结构
- JavaVM:进程级虚拟机实例
- JNIEnv:线程级接口指针
-
调用链路
- Java → Native:native方法声明 → JNI层转换 → Native实现
- Native → Java:JNIEnv::Call*Method → 使用缓存的methodID
-
生命周期管理
- Native对象通过long指针传递给Java层
- 使用强引用计数管理对象生命周期
- 全局引用用于长期持有Java对象
-
性能优化
- 缓存字段和方法ID
- 正确管理本地/全局引用
- 减少JNI边界跨越
- 批量处理数据
8.2 Android Framework的JNI设计哲学
通过对Android 14源码的深入分析,我们可以总结出Framework层JNI设计的核心原则:
- 职责分离:Java层处理业务逻辑,Native层处理系统调用和性能关键路径
- 统一管理:通过AndroidRuntime集中管理所有JNI注册
- 性能优先:大量使用缓存、批量处理、引用计数等优化手段
- 健壮性:完善的异常处理、引用管理、线程安全机制
8.3 实践建议
对于Android开发者和系统工程师:
- 遵循Framework的注册模式:使用动态注册,集中管理
- 重视性能优化:缓存ID、批量处理、减少跨越
- 严格的资源管理:正确使用本地/全局引用,避免内存泄漏
- 防御性编程:完善异常检查,确保线程安全
附录:源码索引
| 主题 | 源码路径 | 关键行号 |
|---|---|---|
| AndroidRuntime初始化 | frameworks/base/core/jni/AndroidRuntime.cpp |
1236-1251 |
| JNI注册表 | frameworks/base/core/jni/AndroidRuntime.cpp |
1499-1650 |
| startReg函数 | frameworks/base/core/jni/AndroidRuntime.cpp |
1669-1698 |
| MessageQueue JNI实现 | frameworks/base/core/jni/android_os_MessageQueue.cpp |
全文 |
| MessageQueue Java声明 | frameworks/base/core/java/android/os/MessageQueue.java |
69-75 |
| JNI方法注册 | frameworks/base/core/jni/android_os_MessageQueue.cpp |
246-267 |
| Native回调Java | frameworks/base/core/jni/android_os_MessageQueue.cpp |
157-178 |