android jni framework,Android Framework层的JNI机制(一)

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和data文件系统。

运行各种服务,主要为各种守护进程。

本地部分启动完成,形成一系列守护进程,其中名称为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(env);

ALOGV("Registering %s's %d native methods...", className, numMethods);

scoped_local_ref 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函数的指针,从而开始函数调用,大大提高执行效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值