《Android的设计与实现:卷I》——第2章 2.2.3Log系统的JNI方法注册

    xiaoxiao2021-04-17  196

    2.2.3 Log系统的JNI方法注册

    JNI层已经实现了Java层声明的Native方法。可这两个方法又是如何联系在一起的呢?我们接着分析android_util_Log.cpp的源码。定位到以下部分:static JNINativeMethod gMethods[] = { { "isLoggable", "(Ljava/lang/String;I)Z",

    (void) android_util_Log_isLoggable },

    { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I",

    (void*) android_util_Log_println_native },

    };这里定义了一个数组gMethods,用来存储JNINativeMethod类型的数据。 可以在jni.h文件中找到JNINativeMethod的定义:

    可见,JNINativeMethod是一个结构体类型,保存了声明函数和实现函数的一一对应关系。

    下面分析gMethods[0]中存储的对应信息:{ "isLoggable", "(Ljava/lang/String;I)Z", (void) android_util_Log_isLoggable }Java层声明的Native函数名为isLoggable。Java层声明的Native函数的签名为(Ljava/lang/String;I)Z。JNI层实现方法的指针为(void) android_util_Log_isLoggable。

    至此,我们给出了Java层方法和JNI层方法的对应关系。可如何告诉虚拟机这种对应关系呢?继续分析android_util_Log.cpp源码。定位到以下部分:int register_android_util_Log(JNIEnv env){

    jclass clazz = env->FindClass("android/util/Log");

    levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz,

    "DEBUG", "I")); …… return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));

    这个函数最后调用了AndroidRuntime::registerNativeMethods,并将gMethods数组、Java层类名以及一个JNIEnv类型的指针一同传给registerNativeMethods。

    初次接触JNI的读者可能有这样的疑问:这是在做什么?JNIEnv是什么?为什么找遍android_util_Log.cpp源码都没找到这个函数是在哪里调用的?

    我们先来分析这个函数是做什么的。

    第一个问题:registerNativeMethods函数的作用是什么?

    既然registerNativeMethods是AndroidRuntime中定义的函数,打开AndroidRuntime.cpp文件,定位到registerNativeMethods函数,其代码如下:int AndroidRuntime::registerNativeMethods(JNIEnv env,

    const char className, const JNINativeMethod gMethods, int numMethods)

    {

    return jniRegisterNativeMethods(env, className, gMethods, numMethods);

    }这里仅仅是对jniRegisterNativeMethods的封装,可以在头文件JNIHelp.h中找到该方法的定义://注册特定类的一个或多个Native方法

    int jniRegisterNativeMethods(C_JNIEnv env, const char className,const JNINativeMethod gMethods, int numMethods);接着看这个方法是怎么实现的。打开JNIHelp.cpp,代码如下:extern "C" int jniRegisterNativeMethods(C_JNIEnv env, const char* className,

    const JNINativeMethod* gMethods, int numMethods)

    {

    JNIEnv e = reinterpret_cast<JNIEnv*>(env); scoped_local_ref<jclass> c(env, findClass(env, className)); …… if ((env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { LOGE("RegisterNatives failed for '%s', aborting", className); abort(); } return 0;

    }这里最终调用了JNIEnv的RegisterNatives函数,将gMethods中存储的方法关联信息传递给Dalvik虚拟机。在jni.h中找到RegisterNatives的方法原型如下:jint RegisterNatives(JNIEnv env, jclass clazz,? const JNINativeMethod *methods,

    jint nMethods);

    其作用是向clazz参数指定的类注册本地方法。这样,虚拟机就得到了Java层和JNI层之间的对应关系,就可以实现Java和C/C++代码的互操作了。

    第二个问题:JNIEnv是什么?

    为了不打断读者的思路,将在下节详细介绍JNIEnv是什么。这里读者只需要知道,JNIEnv是一个指针,指向了一组JNI函数,通过这组函数可以在JNI层操作Java对象,以此实现Java层和native层互操作。

    第三个问题:register_android_util_Log函数是在哪里调用的?

    这个问题涉及JNI部分代码在系统启动过程中是如何加载的,这已经超出了本章的知识范围,我们将在启动篇详细介绍这个过程。在这里,读者只需要知道这个函数是在系统启动过程中通过AndroidRuntime.cpp的register_jni_procs方法执行的,进而调用到register_android_util_Log将这种函数映射关系注册给Dalvik虚拟机的。

    注意 使用JNI有两种方式:一种是遵守JNI规范的函数命名规范,建立声明函数和实现函数之间的对应关系;另一种是就是Log系统中采用的函数注册方式。应用层多采用第一种方式,应用框架层多采用第二种方式。

    至此,Log系统就可以正常使用JNI提供的接口访问系统底层提供的服务了。下面,将对Log系统中涉及的JNI技术进行详细讲解。

    相关资源:七夕情人节表白HTML源码(两款)

    最新回复(0)