本文以 android-12.0.0_r34 的代码进行分析。
framework中的JNI
通常来说,Android framework 中使用到的 native 函数都是动态注册的,而且注册过程有固定的套路。我们以Parcel
类为例来解析套路。
首先,我们在Parcel.java
中会看到很多标记为 native 的方法(为了便于演示,去掉了很多 native 方法):
java">public final class Parcel {private static native long nativeCreate();private static native long nativeFreeBuffer(long nativePtr);private static native void nativeDestroy(long nativePtr);
}
然后,你可以在 Android 源码中找到相应的 jni 定义:
// frameworks/base/core/jni/android_os_Parcel.cpp
#include "core_jni_helpers.h"namespace android {static struct parcel_offsets_t
{jclass clazz;jfieldID mNativePtr;jmethodID obtain;jmethodID recycle;
} gParcelOffsets;static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{Parcel* parcel = new Parcel();return reinterpret_cast<jlong>(parcel);
}static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {parcel->freeData();}
}static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);delete parcel;
}
}// ----------------------------------------------------------------------------static const JNINativeMethod gParcelMethods[] = {{"nativeCreate", "()J", (void*)android_os_Parcel_create},{"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer},{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
};const char* const kParcelPathName = "android/os/Parcel";int register_android_os_Parcel(JNIEnv* env)
{jclass clazz = FindClassOrDie(env, kParcelPathName);gParcelOffsets.clazz = MakeGlobalRefOrDie(env, clazz);gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, clazz, "mNativePtr", "J");gParcelOffsets.obtain = GetStaticMethodIDOrDie(env, clazz, "obtain", "()Landroid/os/Parcel;");gParcelOffsets.recycle = GetMethodIDOrDie(env, clazz, "recycle", "()V");return RegisterMethodsOrDie(env, kParcelPathName, gParcelMethods, NELEM(gParcelMethods));
}};
首先,我们会发现 Java 层定义的 native 方法的名字中都带有 native 前缀,但实际上,它们对应的 cpp 函数的名称都不会带有 native 前缀。
其次,源码中习惯上会使用一个全局 static 数组来统一存放 cpp 函数与 Java 层定义的映射,然后,再定义一个register_xxx
的函数,此函数内部一般执行两件事,第一件是完成这个文件内所有 JNI 函数的动态注册;第二件是提前使用JNIEnv
找到后续 cpp 函数回调 Java 层时可能要用到的jclass
、jfieldID
或jmethodID
,这些信息会保存在另一个全局结构体中。
RegisterMethodsOrDie
动态注册的关键在于RegisterMethodsOrDie
这个函数,这个函数实际上来自于frameworks/base/core/jni/core_jni_helpers.h
(正因为在同一个目录下,因此 include 的时候使用了双引号)
// frameworks/base/core/jni/core_jni_helpers.h#include <android_runtime/AndroidRuntime.h>
namespace android {static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,const JNINativeMethod* gMethods, int numMethods) {int res = AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods);LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");return res;
}
}
我们继续追踪AndroidRuntime.h
,这个头文件位于frameworks/base/core/jni/include/android_runtime/AndroidRuntime.h
,实现位于frameworks/base/core/jni/AndroidRuntime.cpp
// frameworks/base/core/jni/AndroidRuntime.cpp#include <nativehelper/JNIHelp.h>
namespace android {
/** Register native methods using JNI.*/
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,const char* className, const JNINativeMethod* gMethods, int numMethods)
{return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
}
jniRegisterNativeMethods
这个方法实际上由JNIHelp.h
导入,实现位于libnativehelper/JNIHelp.c
// libnativehelper/JNIHelp.cint jniRegisterNativeMethods(JNIEnv* env, const char* className,const JNINativeMethod* methods, int numMethods)
{ALOGV("Registering %s's %d native methods...", className, numMethods);jclass clazz = (*env)->FindClass(env, className);ALOG_ALWAYS_FATAL_IF(clazz == NULL,"Native registration unable to find class '%s'; aborting...",className);int result = (*env)->RegisterNatives(env, clazz, methods, numMethods);(*env)->DeleteLocalRef(env, clazz);if (result == 0) {return 0;}// 剩下的代码与失败处理相关, 省略// ...ALOGF("RegisterNatives failed for '%s'; aborting...", className);return result;
}
何时调用register
摸清楚了 Android framework 如何动态注册 JNI,另一个问题便是 framework 会在什么时候调用相应的 register 函数呢?答案依然在frameworks/base/core/jni/AndroidRuntime.cpp
中。
首先,我们会在 AndroidRuntime.cpp 中看到 extern 声明和一个全局数组:
// frameworks/base/core/jni/AndroidRuntime.cppusing namespace android;// 不定义在android这个命名空间内的register函数, 数量不少, 这里仅展示一小部分
extern int register_android_opengl_jni_EGL14(JNIEnv* env);
extern int register_android_opengl_jni_EGL15(JNIEnv* env);
extern int register_android_opengl_jni_EGLExt(JNIEnv* env);
// ...namespace android {
/** JNI-based registration functions. Note these are properly contained in* namespace android.*/
// 省略...
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
// 省略...// 定义了REG_JNI, 如果没开启debug, 这个结构体就不会存储名字
#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
static const RegJNIRec gRegJNI[] = {REG_JNI(register_android_os_Parcel),REG_JNI(register_android_opengl_jni_EGL14),REG_JNI(register_android_opengl_jni_EGL15),REG_JNI(register_android_opengl_jni_EGLExt),REG_JNI(register_android_os_MessageQueue)
};
}
gRegJNI
这个数组基本上约等于存放了一堆 lambda,等待Android 虚拟机启动时,直接遍历该数组就可以完成注册。其调用链大致为:start -> startReg -> register_jni_procs
// frameworks/base/core/jni/AndroidRuntime.cpp/** Start the Android runtime. This involves starting the virtual machine* and calling the "static void main(String[] args)" method in the class* named by "className".** Passes the main function two arguments, the class name and the specified* options string.*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{// ...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;}// ...
}/** Register android native functions with the VM.*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{ATRACE_NAME("RegisterAndroidNatives");/** This hook causes all future threads created in this process to be* attached to the JavaVM. (This needs to go away in favor of JNI* Attach calls.)*/androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);ALOGV("--- registering native functions ---\n");/** Every "register" function calls one or more things that return* a local reference (e.g. FindClass). Because we haven't really* started the VM yet, they're all getting stored in the base frame* and never released. Use Push/Pop to manage the storage.*/env->PushLocalFrame(200);if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);//createJavaThread("fubar", quickTest, (void*) "hello");return 0;
}static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{for (size_t i = 0; i < count; i++) {if (array[i].mProc(env) < 0) {
#ifndef NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}