Android Framework源码中JNI方法注册流程

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注册方法的流程就彻底完成了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容