JNI(Java Native Interface)Java本地接口。最初对JNI的了解,仅仅停留在Java通过JNI可以实现对C/C++函数的调用。比如,首先在Java中写好native方法。然后在C或C++中文件中,定义一个对应的函数,在这个函数中,实现自己的代码或者调用其他的标准库。最后加载一下生成的动态库,便可以开始使用这个native方法。像是在照葫芦画瓢,知其然,不知其所以然。最近在看Framework层中JNI相关的代码,加上网上大咖的神贴,综合理解,先以Framework中Log为研究对象,分析JNI在其中的使用。
概要
- JNI机制基本要点
- Android中JNI的存在方式
- Framework层Log的JNI使用
一、JNI机制基本要点
用过JNI的工程师,都接触过下面这些知识点:
- JavaVM:表示Java虚拟机。
- JNIEnv:表示JNI环境的上下文,例如注册、查找类、异常等。
- jclass:在JNI中表示的Java类。
- jmethodID:在JNI中表示的Java类中的方法。
- jfiledID:在JNI中表示的Java类中的属性。
- 线程:JNI中通过AttachCurrentThread和DetachCurrentThread方法,实现和Java线程的结合。
它们都在一个叫jni.h的头文件中,这个头文件是JNI机制中很重要的一个头文件。
源代码路径:/libnativehelper/include/nativehelper/jni.h。
在libnativehelper目录下的源文件,编译后会生成一个libnativehelper.so的动态库。其实,jni.h是Android根据Java本地调用的标准写成的一个头文件,在它里面包括了基本类型(类型的映射),以及JavaVM,JNIEnv,jclass,jmethodID,jfiledID等数据结构的定义。
JavaVM对应于jni.h中JNIInvokeInterface结构体,表示虚拟机。JNIEnv对应于JNINativeInterface结构体,表示JNI的环境。在JNI的使用过程中,所调用的功能大都来自JNINativeInterface结构体。例如,处理Java属性和方法的查找,Java属性的访问,Java方法的调用等功能。另外,在JNINativeInterface结构体中,涉及到的一个JNINativeMethod结构体,它表示在本地实现的一个方法,即native方法,后面进行JNI注册的时候会用到。
二、Android中JNI的存在方式
Android中JNI的存在方式主要分两种: 框架层和应用层的JNI使用。不对应用层的使用情况进行介绍,主要目的还是看看框架层里面的JNI。
在Android框架中,JNI库是一些普通的本地动态库,被放置在目标系统的/system/lib目录中。
Java框架层,最主要的JNI内容源代码路径为:/frameworks/base/core/jni。
这里面的代码会生成一个libandroid_runtiem.so的动态库。接下来要分析的Log中JNI的使用,就在这个目录之中。
三、Framework层Log的JNI使用
- Java框架层的Log
在编程的时候,大家都用过Log,其实这个我们经常使用的Log工具,在Java框架层最终调用的是native方法。
贴上源码的路径:/frameworks/base/core/java/android/util/Log.java。
如果感兴趣,可以进去瞧一瞧。咱们以Log.java中的println_native()这个本地方法,进行分析。
- Log的JNI实现
Log的JNI的实现是在一个叫android_util_Log.cpp的源文件中。
源码路径:
头文件:/frameworks/base/core/jni/android_util_Log.h。
源文件:/frameworks/base/core/jni/android_util_Log.cpp。
在android_util_Log.cpp源文件中,我们可以找到println_native的身影。
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "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 },
{ "logger_entry_max_payload_native", "()I", (void*) android_util_Log_logger_entry_max_payload_native },
};
JNINativeMethod是前面提到的一个结构体,这个结构体表示一个实现的本地方法。这个结构体在jni.h文件中定义,内容如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
它有三个指针变量,第一个是字符型指针,可以表示一个字符串,即native方法的名称;第二个也是字符型指针,同样可以表示一个字符串,代表这个native方法的参数和返回值(有特殊的表示方法);第三个是一个未指定类型指针,表示一个函数指针,指向这个native方法对应的jni函数。
有了对JNINativeMethod了解,就可以理解println_native在android_util_Log.cpp源文件中的含义了。其对应jni实现函数是android_util_Log_println_native()。在jni实现函数中,又调用了__android_log_buf_write()这个方法,__android_log_buf_write是本地框架层(非Java框架层)基础的C库之上,Android最底层的本地Log库。
Log库的源码路径为:
头文件:system/core/include/cutils/log.h。
源文件:system/core/liblog。
编译后会生成liblog.so动态库和liblog.a静态库。
- Log的JNI注册
int register_android_util_Log(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, "android/util/Log");
levels.verbose = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "VERBOSE", "I"));
levels.debug = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "DEBUG", "I"));
levels.info = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "INFO", "I"));
levels.warn = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "WARN", "I"));
levels.error = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ERROR", "I"));
levels.assert = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ASSERT", "I"));
return RegisterMethodsOrDie(env, "android/util/Log", gMethods, NELEM(gMethods));
}
这是android_util_Log.cpp源文件中对jni的注册,可以看到RegisterMethodsOrDie()这个方法的调用,传入了我们前面看到的gMethods数组,进行JNI注册。到这里结束了吗?确实第一次看到这里的时候,以为就此结束了。然而,是谁调用了register_android_util_Log()这个方法?在RegisterMethodsOrDie()这个函数里面又做了什么呢?
- register_android_util_Log()函数
register_android_util_Log()这个方法只在android_util_Log.cpp源文件中进行定义,需要找到谁对它进行了调用,才好进一步理解Log的JNI的注册过程。Android源码环境有一个非常不错的方法,可以通过字符串,找到出现过的文件。
指令:cgrep 'register_android_util_Log'
类似于:find . -type f -name "*.cpp" | xargs grep "register_android_util_Log"
通过结果可以发现一个AndoridRuntime.cpp,这是个什么鬼?只能说它很强,是系统运行时的工具类,为Android的运行提供支持。JNI的部分封装也在这个类中。
源码路径:
/frameworks/base/include/android_runtime/AndroidRuntime.h。
/frameworsk/base/core/jni/AndroidRuntime.cpp。
可以发现,它也是在/frameworsk/base/core/jni目录下,说明也是在libandroid_runtime.so动态库中。
在AndroidRuntime.cpp源文件gRegJNI数组中,发现了register_android_util_Log方法。
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
//省略
}
然后,又在AndroidRuntime.cpp源文件的AndroidRuntime::startReg()这个方法中,使用了gRegJNI这个数组。
/*
* 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;
}
按源代码注释的意思是,这里在向虚拟机注册本地方法。同样在AndroidRuntime.cpp源文件中,AndroidRuntime::start()中又调用了AndroidRuntime::startReg(),到这里,需要继续往下看,就得找找是谁调用了AndroidRuntime::start()方法。然而,要想知道谁调用了它,已经涉及到zygote这一块的知识。
- zygote
zygote是通过init进程读取init.rc启动的一个守护进程的名称。如果从最下面开始说,得再介绍一下Android启动流程的本地阶段,这一块,属于扩充了解。
Android启动流程的本地阶段:
- BootLoader运行,Linux通用内容。
- Linux内核运行,Linux通用内容,通常是二进制形式代码形式存在。
- 内核加载根文件系统,Linux通用内容。
- init进程运行,用户空间的第一个进程。
- 运行init.rc脚本。
- 加载system和date文件系统。
- 运行各种服务,主要为各种守护进程。
本地部分启动完成,形成一系列守护进程,其中名称为zygote的守护进程,将继续完成Java部分的初始化。
Java部分的启动流程:
- 从本地可执行程序运行名为zygote的守护进程。
- zygote运行ZygoteInit(进入Java程序)。
- ZygoteInit运行SystemServer(Java类),并分裂出新的进程。
- SystemServer首先运行libandroid_servers.so库当中的初始化(进入本地程序)。
- 执行libanroid_servers.so当中的系统初始化。
SystemServer中的Java初始化再次被调用(再入Java程序)。 - 建立ServerThread线程。
- ServerThread线程建立各个服务,然后进入循环。
- ActivityManagerService在启动结束发送相关信息。
- 各个Java应用程序运行。
这些是引用资料书中的知识,想详细了解,可以看看《Android核心原理及系统级应用高效开发》或《深入理解Android系统》。回到我们的zygote进程,init.rc中包含了一个init.${ro.zygote}.rc。 init.rc
和init.${ro.zygote}.rc
源码路径:/system/core/rootdir。
在init.zygote32.rc中,相关内容如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
这个是Android系统中的特殊语法,它启动了一个名称为zygote的进程,也就是/system/bin/app_process这个可执行程序。
源码路径为:/frameworks/base/cmds/app_process。
在这个目录下,有一个app_main.cpp的源文件,其中相关的代码如下:
// 省略
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
在app_main.cpp源文件中,有一个AppRuntime的类,它继承了AndroidRuntime。runtime是AppRuntime类的一个实例,runtime.start()相当于调用了AndroidRuntime::start()这个方法,至此,前后就连接起来了。概括的说,系统启动zygote进程时,会调用AndroidRuntime::start()方法,接着调用AndroidRuntime::startReg(),然后调用到了register_android_util_Log()这个方法。剩下最后一个问题,register_android_util_Log()被调用后,在它方法体中的RegisterMethodsOrDie()函数做了什么?
- RegisterMethodsOrDie()函数
RegisterMethodsOrDie()这个方法是/frameworks/base/core/jni/core_jni_helpers.h中声明的一个方法。
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这个类,调用了它的registerNativeMethods()方法,并最终调用了jniRegisterNativeMethods()进行本地方法的注册。而jniRegisterNativeMethods()是/libnativehelper/JNIHelp.cpp源文件中的方法,它里面内容为:
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
ALOGV("Registering %s's %d native methods...", className, numMethods);
scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
char* tmp;
const char* msg;
if (asprintf(&tmp,
"Native registration unable to find class '%s'; aborting...",
className) == -1) {
// Allocation failed, print default warning.
msg = "Native registration unable to find class; aborting...";
} else {
msg = tmp;
}
e->FatalError(msg);
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
char* tmp;
const char* msg;
if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
// Allocation failed, print default warning.
msg = "RegisterNatives failed; aborting...";
} else {
msg = tmp;
}
e->FatalError(msg);
}
return 0;
}
这段代码可以看出,通过调用(*env)的RegisterNatives指针函数,进行了JNI注册。所以最后的动作是交给了JNINativeInterface结构体所表示的JNI环境执行。当在Java层调用native方法时,不需要依据native方法包和名称寻找对应的JNI函数。而是可以通过已经注册的映射关系,快速找到对应的JNI函数的指针,从而开始函数调用,大大提高执行效率。
网友评论