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 voIDandroID_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()方式注册。
文件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实例
以上是内存溢出为你收集整理的JNI原理全部内容,希望文章能够帮你解决JNI原理所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)