JNI方法注册方式
Android系统在启动启动过程中,先启动Kernel创建init进程,紧接着由init进程fork第一个横穿Java和C/C++的进程,即Zygote进程。Zygote启动过程中会AndroidRuntime.cpp中的startVm创建虚拟机,VM创建完成后,紧接着调用startReg完成虚拟机中的JNI方法注册。
1.android系统启动时候已经预注册好
这种多出现在android自己系统代码里提供的注册方法
startReg
AndroidRuntime.cpp
int AndroidRuntime::startReg(JNIEnv* env)
{
//设置线程创建方法为javaCreateThreadEtc
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
env->PushLocalFrame(200);
//进程JNI方法的注册
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
return 0;
}
register_jni_procs(gRegJNI, NELEM(gRegJNI), env)这行代码的作用就是就是循环调用gRegJNI数组成员所对应的方法。
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) {
return -1;
}
}
return 0;
}
gRegJNI数组,有100多个成员变量,定义在AndroidRuntime.cpp:
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_Binder),
...
};
2.应用程序自己注册jni
以MediaPlayer.java为例,其包名为android.media:
public class MediaPlayer{
static {
System.loadLibrary("media_jni");
native_init();
}
private static native final void native_init();
...
}
通过static静态代码块中System.loadLibrary方法来加载动态库,库名为media_jni, Android平台则会自动扩展成所对应的libmedia_jni.so库。 接着通过关键字native加在native_init方法之前,便可以在java层直接使用native层方法。
接下来便要查看libmedia_jni.so库定义所在文件,一般都是通过Android.mk文件定义LOCAL_MODULE:= libmedia_jni,可以采用grep或者mgrep来搜索包含libmedia_jni字段的Android.mk所在路径。
libmedia_jni.so位于/frameworks/base/media/jni/Android.mk
该Android.mk所在目录/frameworks/base/media/jni/中找到android_media_MediaPlayer.cpp文件,并在文件中存在相应的方法:
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
...
}
总结:
JNI注册的两种时机:
1.Android系统启动过程中Zygote注册,可通过查询AndroidRuntime.cpp中的gRegJNI,看看是否存在对应的register方法;
2.调用System.loadLibrary()方式注册。
JNI原理分析
文件MediaPlayer.java中调用System.loadLibrary(“media_jni”),把libmedia_jni.so动态库加载到内存。以loadLibrary为起点展开JNI注册流程的过程分析。
[System.java]
loadLibrary
public static void loadLibrary(String libName) {
//调用Runtime方法
Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}
[Runtime.java]
void loadLibrary(String libraryName, ClassLoader loader) {
if (loader != null) {
String filename = loader.findLibrary(libraryName);
//加载库
String error = doLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
//loader为空,则会进入该分支
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
for (String directory : mLibPaths) {
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
//加载库
String error = doLoad(candidate, loader);
if (error == null) {
return; //加载成功
}
}
}
...
}
真正加载的工作是由doLoad()
doLoad
该方法内部增加同步锁,保证并发时一致性。
private String doLoad(String name, ClassLoader loader) {
...
synchronized (this) {
return nativeLoad(name, loader, ldLibraryPath);
}
}
nativeLoad()这是一个native方法,再进入ART虚拟机的java_lang_Runtime.cc,最终的核心功能工作:
调用dlopen函数,打开一个so文件并创建一个handle;
调用dlsym()函数,查看相应so文件的JNI_OnLoad()函数指针,并执行相应函数。
总之,System.loadLibrary()的作用就是调用相应库中的JNI_OnLoad()方法。接下来说说JNI_OnLoad()过程。
JNI_OnLoad
[-> android_media_MediaPlayer.cpp]
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
//注册JNI方法
if (register_android_media_MediaPlayer(env) < 0) {
goto bail;
}
...
}
register_android_media_MediaPlayer
[-> android_media_MediaPlayer.cpp]
static int register_android_media_MediaPlayer(JNIEnv *env) {
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
其中gMethods,记录java层和C/C++层方法的一一映射关系。
static JNINativeMethod gMethods[] = {
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
{"_release", "()V", (void *)android_media_MediaPlayer_release},
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
...
};
这里涉及到结构体JNINativeMethod,其定义在jni.h文件:
typedef struct {
const char* name; //Java层native函数名
const char* signature; //Java函数签名,记录参数类型和个数,以及返回值类型
void* fnPtr; //Native层对应的函数指针
} JNINativeMethod;
registerNativeMethods
[-> AndroidRuntime.cpp]
int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
jniRegisterNativeMethods该方法是由Android JNI帮助类JNIHelp.cpp来完成。
jniRegisterNativeMethods
[-> 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 (c.get() == NULL) {
e->FatalError("");//无法查找native注册方法
}
// 调用JNIEnv结构体的成员变量
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
e->FatalError("");//native方法注册失败
}
return 0;
}
RegisterNatives
[-> jni.h]
struct _JNIEnv {
const struct JNINativeInterface* functions;
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
jint nMethods)
{ return functions->RegisterNatives(this, clazz, methods, nMethods); }
...
}
functions是指向JNINativeInterface结构体指针,也就是将调用下面方法:
struct JNINativeInterface {
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
...
}
这个过程完成了gMethods数组中的方法的映射关系,比如java层的native_init()方法,映射到native层的android_media_MediaPlayer_native_init()方法。
虚拟机相关的变量中有两个非常重要的量JavaVM和JNIEnv:
JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例