JNI原理

JNI原理,第1张

概述JNI方法注册方式Android系统在启动启动过程中,先启动Kernel创建init进程,紧接着由init进程fork第一个横穿Java和C/C++的进程,即Zygote进程。Zygote启动过程中会AndroidRuntime.cpp中的startVm创建虚拟机,VM创建完成后,紧接着调用startReg完成虚拟机中的JNI方法注册。1.android系 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 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()方式注册。

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实例

总结

以上是内存溢出为你收集整理的JNI原理全部内容,希望文章能够帮你解决JNI原理所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1029364.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存