在 Android Zygote进程启动(一) 文章中提到在AndroidRuntime.cpp 中提到 AndroidRuntime::start() 函数主要做的三件事:1.启动java虚拟机、2.注册jni方法 3.调用zygote的main方法。今天就来AndroidFramework中是如何注册JNI方法的。
/*
* 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;
}
方法中首先设置了线程创建方法为javaCreateThreadEtc ,然后通过register_jni_procs注册了jni的方法。
register_jni_procs 函数参数RegJNIRec是预先定义的一个宏:
#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
从代码上看可以看出来RegJNIRec 是一个结构体,里面包含了一个名字叫mProc接收参数为JNIEnv指针返回值为int的函数指针。
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
····
}
register_jni_procs 函数循环调用RegJNIRec数组中的mProc方法进行jni方法的注册。
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 NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
}
这里用register_android_content_AssetManager 这个方法的注册来分析注册过程。首先register_android_content_AssetManager方法的声明的之处位于命名空间android之下,代码如下:
namespace android {
/*
* JNI-based registration functions. Note these are properly contained in
* namespace android.
*/
extern int register_android_app_admin_SecurityLog(JNIEnv* env);
extern int register_android_content_AssetManager(JNIEnv* env);
而相应的实现操作写在android_util_AssetManager.cpp 文件中
int register_android_content_AssetManager(JNIEnv* env)
{
......
return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
NELEM(gAssetManagerMethods));
}
gAssetManagerMethods是一个当前需要注册的jni的方法列表:
static const JNINativeMethod gAssetManagerMethods[] = {
/* name, signature, funcPtr */
// Basic asset stuff.
{ "openAsset", "(Ljava/lang/String;I)J",
(void*) android_content_AssetManager_openAsset },
{ "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*) android_content_AssetManager_openAssetFd },
{ "openNonAssetNative", "(ILjava/lang/String;I)J",
(void*) android_content_AssetManager_openNonAssetNative },
{ "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*) android_content_AssetManager_openNonAssetFdNative },
{ "list", "(Ljava/lang/String;)[Ljava/lang/String;",
(void*) android_content_AssetManager_list },
{ "destroyAsset", "(J)V",
(void*) android_content_AssetManager_destroyAsset },
{ "readAssetChar", "(J)I",
(void*) android_content_AssetManager_readAssetChar },
{ "readAsset", "(J[BII)I",
(void*) android_content_AssetManager_readAsset },
{ "seekAsset", "(JJI)J",
(void*) android_content_AssetManager_seekAsset },
{ "getAssetLength", "!(J)J",
(void*) android_content_AssetManager_getAssetLength },
{ "getAssetRemainingLength", "!(J)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
{ "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
{ "addOverlayPathNative", "(Ljava/lang/String;)I",
(void*) android_content_AssetManager_addOverlayPath },
{ "isUpToDate", "()Z",
(void*) android_content_AssetManager_isUpToDate },
// Resources.
{ "getLocales", "()[Ljava/lang/String;",
(void*) android_content_AssetManager_getLocales },
{ "getNonSystemLocales", "()[Ljava/lang/String;",
(void*) android_content_AssetManager_getNonSystemLocales },
{ "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
(void*) android_content_AssetManager_getSizeConfigurations },
{ "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
(void*) android_content_AssetManager_setConfiguration },
{ "getResourceIdentifier","!(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*) android_content_AssetManager_getResourceIdentifier },
{ "getResourceName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceName },
{ "getResourcePackageName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourcePackageName },
{ "getResourceTypeName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceTypeName },
{ "getResourceEntryName","!(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceEntryName },
{ "loadResourceValue","!(ISLandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceValue },
{ "loadResourceBagValue","!(IILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceBagValue },
{ "getStringBlockCount","!()I",
(void*) android_content_AssetManager_getStringBlockCount },
{ "getNativeStringBlock","!(I)J",
(void*) android_content_AssetManager_getNativeStringBlock },
{ "getCookieName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getCookieName },
{ "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
(void*) android_content_AssetManager_getAssignedPackageIdentifiers },
// Themes.
{ "newTheme", "()J",
(void*) android_content_AssetManager_newTheme },
{ "deleteTheme", "(J)V",
(void*) android_content_AssetManager_deleteTheme },
{ "applyThemeStyle", "(JIZ)V",
(void*) android_content_AssetManager_applyThemeStyle },
{ "copyTheme", "(JJ)V",
(void*) android_content_AssetManager_copyTheme },
{ "clearTheme", "(J)V",
(void*) android_content_AssetManager_clearTheme },
{ "loadThemeAttributeValue", "!(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
{ "getThemeChangingConfigurations", "!(J)I",
(void*) android_content_AssetManager_getThemeChangingConfigurations },
{ "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
(void*) android_content_AssetManager_dumpTheme },
{ "applyStyle","!(JIIJ[I[I[I)Z",
(void*) android_content_AssetManager_applyStyle },
{ "resolveAttrs","!(JII[I[I[I[I)Z",
(void*) android_content_AssetManager_resolveAttrs },
{ "retrieveAttributes","!(J[I[I[I)Z",
(void*) android_content_AssetManager_retrieveAttributes },
{ "getArraySize","!(I)I",
(void*) android_content_AssetManager_getArraySize },
{ "retrieveArray","!(I[I)I",
(void*) android_content_AssetManager_retrieveArray },
// XML files.
{ "openXmlAssetNative", "(ILjava/lang/String;)J",
(void*) android_content_AssetManager_openXmlAssetNative },
// Arrays.
{ "getArrayStringResource","(I)[Ljava/lang/String;",
(void*) android_content_AssetManager_getArrayStringResource },
{ "getArrayStringInfo","!(I)[I",
(void*) android_content_AssetManager_getArrayStringInfo },
{ "getArrayIntResource","!(I)[I",
(void*) android_content_AssetManager_getArrayIntResource },
{ "getStyleAttributes","!(I)[I",
(void*) android_content_AssetManager_getStyleAttributes },
// Bookkeeping.
{ "init", "(Z)V",
(void*) android_content_AssetManager_init },
{ "destroy", "()V",
(void*) android_content_AssetManager_destroy },
{ "getGlobalAssetCount", "()I",
(void*) android_content_AssetManager_getGlobalAssetCount },
{ "getAssetAllocations", "()Ljava/lang/String;",
(void*) android_content_AssetManager_getAssetAllocations },
{ "getGlobalAssetManagerCount", "()I",
(void*) android_content_AssetManager_getGlobalAssetManagerCount },
};
这里Andoird 中使用了一种不同传统JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种 Java和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义在jni.h的头文件中如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针,指向C函数。
其中比较难以理解的是第二个参数,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下
字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring
Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
RegisterMethodsOrDie方法的实现:
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;
}
可以看出,最终是将所有jni的native进行注册。
至此Android Framework 中JNI注册方法的流程就彻底完成了。