深入理解android卷一之jni小结

1.jni简介

native c/c++代码是和平台相关联的,是可以直接运行的,在java出现以前很多软件由native语言编写,包括Jvm也是由native语言编写的,java提供了一套jni技术使得native和java相互调用。

Jni功能图示

根据oracle文档,如下情况需要使用jni:

  • The standard Java class library does not support the platform-dependent features needed by the application.
  • You already have a library written in another language, and wish to make it accessible to Java code through the JNI.
  • You want to implement a small portion of time-critical code in a lower-level language such as assembly.

使用jni可以:

  • Create, inspect, and update Java objects (including arrays and strings).
  • Call Java methods.
  • Catch and throw exceptions.
  • Load classes and obtain class information.
  • Perform runtime type checking.

使用jni的Invoication API可以把jvm嵌入native application中,而无需接触jvm的源码。

2.native函数和java函数关联映射

jni java部分,MediaScanner.java:

public class MediaScanner
{ //①加载对应的JNI库,media_jni是JNI库的名字。实际加载动态库的时候会拓展成libmedia_jni.so,在Windows平台上将拓展为media_jni.dll。
static{
    System.loadLibrary("media_jni");
    native_init();//调用native_init函数
 }
//②声明一个native函数。native为Java的关键字,表示它将由JNI层完成,java仅声明函数即可。
private static native final void native_init();
private native void processFile(String path, String mimeType,MediaScannerClient client);
}

jni native部分,android_media_MediaScanner.cpp:

//①这个函数是native_init的JNI层实现。
static void android_media_MediaScanner_native_init(JNIEnv *env)
{
jclass clazz;
 clazz= env->FindClass("android/media/MediaScanner");
   fields.context = env->GetFieldID(clazz, "mNativeContext","I");
   return;
}
//这个函数是processFile的JNI层实现。
static void android_media_MediaScanner_processFile(JNIEnv*env, jobject thiz, 
jstring path, jstring mimeType, jobject client)
{
    MediaScanner*mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
    constchar *pathStr = env->GetStringUTFChars(path, NULL);
    if(mimeType) {
    env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
}
}

java和native的映射注册:

静态注册:

当第一次使用到某一个native函数的时候jvm会找对应的native函数,如果没有找到native函数,就会报错;如果找到了则建立两者映射关系(一个结构体,分别存储了java方法名和native方法名),以后再调用这个native函数可以直接使用映射关系直接找到了。 初次调用native函数时要根据函数名字搜索对应的JNI层函数来建立关联关系,这样会影响第一次调用的运行效率。动态注册可以解决这个问题。

动态注册:

SystemloadLibrary完成之后会尝试回调对应加载库中的jint JNI_OnLoad(JavaVM* vm, void* reserved)方法,native库可以选择性的实现该方法,在该方法中自行向jvm注册native函数和java函数的映射关系。如此便避免了静态注册的第一次使用还需要查找的问题,这称之为动态注册。

android的libmedia_jni.so的动态注册在android_media_MediaPlayer.cpp中实现。

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
   //该函数的第一个参数类型为JavaVM,这可是虚拟机在JNI层的代表喔,每个Java进程只有一个
   JNIEnv* env = NULL;
    jintresult = -1;
    if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    goto bail;
    }
    //动态注册MediaScanner的JNI函数。
    if(register_android_media_MediaScanner(env) < 0) {
    goto bail;
}
returnJNI_VERSION_1_4;//必须返回大于1.1的值,否则会报错。
}

值得注意的是该方法默认返回JNI_VERSION_1_1,而这个方法是在1_2中被添加的,需要返回1.2及以上版本才可以使用新的方法和特性。

The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary). JNI_OnLoad must return the JNI version needed by the native library.
In order to use any of the new JNI functions, a native library must export a JNI_OnLoad function that returns JNI_VERSION_1_2. If the native library does not export a JNI_OnLoad function, the VM assumes that the library only requires JNI version JNI_VERSION_1_1. If the VM does not recognize the version number returned by JNI_OnLoad, the native library cannot be loaded.

引用自oracle文档网站:

https://docs.oracle.com/javase/8/docs/technotes/guides/jni/jni-12.html#JNI_OnLoad

3.java和jni类型对应及类型签名对应

原始类型:

原始类型对应

引用类型对应:

引用类型对应

类型签名用于使用字符串形式描述方法定义供jvm解析,其对应列表如下:

类型签名对应

4.native函数运行时参数和注意事项

  • jni调用可以理解为同步调用,JniEnv是基于特定线程的。代表函数运行时的上下文环境,jni提供的所有函数方法几乎都需要通过它来使用,需要注意的是不同的线程具有不同的JniEnv,所以不能将其保存为全局引用以期望另外的线程可以访问。如果当前线程还未具有JniEnv,需要先将其绑定到jvm来获取对应的JniEnv,运行完需要解绑线程。
  • jni引用变量分为局部、全局、和全局弱引用。全局引用不会被java的gc回收,全局弱引用存在被回收的可能,局部引用当方法结束后自动被回收,方法参数的引用都是局部引用,方法中定义的引用都是局部引用,局部引用可以手动立刻解除占用提高效率,如下:
for(inti = 0; i < 100; i++)
{
    jstring pathStr = mEnv->NewStringUTF(path);
    ......//做一些操作
    //mEnv->DeleteLocalRef(pathStr); //不立即释放Local Reference会导致创建100个局部引用,直到方法结束才会释放。

}
  • jni中的参数。会比java中的额外多两个参数,第一个参数一定是JniEnv引用,第二个参数根据是否是静态方法有所不同。如果java世界声明该方法是非静态的,则为java对象引用,如果是静态的则为对应的java class引用。

  • jfieldID和jmethodID。对应java世界的类字段和方法,jni提供方法可以在java的方法和jmethodID之间相互转换,jfildD也提供了相关的转换方法。

  • jni中的异常。因为大部分c库不确保运行时参数没有错误,如果确保可能会导致判断两次降低效率,所以参数无误应该有调用者来确保。jni遇见异常之后并不像Java一样终止运行,会记录异常并继续运行,直到从JNI层返回到Java层后,虚拟机才会抛出这个异常。jni函数可以在合适的时刻执行ExceptionCheck()来主动检测有无异常被记录,并选择return来终止函数的运行。此外,jni可以主动抛出任意java世界的异常。jni有两种方式可以处理异常:a.使用ThrowNew抛出自定义异常并使用return返回,或者干脆直接return让java代码抛出对应异常;b.使用ExceptionClear()清除异常,并自己处理掉异常。

Most C library functions do not guard against programming errors. The printf() function, for example, usually causes a runtime error, rather than returning an error code, when it receives an invalid address. Forcing C library functions to check for all possible error conditions would likely result in such checks to be duplicated--once in the user code, and then again in the library.

The programmer must not pass illegal pointers or arguments of the wrong type to JNI functions

参考链接:

https://docs.oracle.com/en/java/javase/11/docs/specs/jni/index.html

http://wiki.jikexueyuan.com/project/deep-android-v1/jni.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339