Jni Tips
1.JavaVM and JNIEnv
JNI有两种关键的数据结构,JavaVM和JNIEnv,两者均为指向VM方法JNI方法的列表的的指针(C++版本中它们是Class,Class的所有成员均为函数指针)。JavaVM提供创建和销毁VM的调用接口,理论上可以创建多个VM,但Android仅仅支持一个VM。JNIEnv提供所有JNI接口函数,Native函数的第一个参数即为JNIEnv。
JNIEnv提供大多数JNI方法,你的所有Native方法都接受一个JNIEnv作为第一个参数。
JNIEnv仅仅用于线程本地使用,不允许多个线程共享一个JNIEnv。
2.Threads
所有Linux线程均被Kernel调度,通常线程起始于托管代码(可理解为Java代码),使用Thread.start方法启动。但线程也可以在任何其他地方被创建,线程创建(使用thread_create)之后可以使用JNI的AttachCurrendThread方法附加到JavaVM中。
注意:新创建的线程在Attach到JavaVM之前,是没有JNIEnv的,不能执行任何JNI调用。
Attaching一个线程到JavaVM时,将使一个java.lang.Thread被创建和添加到main ThreadGroup。对一个已经Attach到JavaVM的线程调用AttachCurrendThread是无效的。
Android不会Suspend执行Native Code的线程,如果垃圾回收正在进行或者调试器在请求Suspend,Android将在线程下一次调用JNI函数时执行Suspend。
Attach到JavaVM的线程需要在其退出前显示调用DetachCurrentThread。如果实现起来有难度,在Android2.0以上系统内可使用pthread_create_key来声明一个析构函数,当线程退出的时候,析构函数将被调用,在析构函数中调用DetachCurrentThread是非常好的选择。
3.jclass,jmethodID,jfieldID
如果你想通过native代码(jni)访问你的对象(java对象),你应该通过下面方式访问
属性或者方法的引用或者IDs需要好几个字符串比较,一旦你拥有它们,你执行它们(指的是方法或者对象)的速度是非常快的。
Find Class、Get methodID fieldID需要花费大量的时间来做字符串比较,推荐的做法是事先缓存这些IDs,以加快调用速度。因为Android限制每个进程只有一个JavaVM,因此在方法内定义static局部变量来缓存这些IDs可有效的提升性能。
类的引用,类的成员ID,和方法ID一直都有效指导类被回收,除了很少情况下,类对象只有没在关联到类加载器的时候才会被垃圾回收机制回收,但是,在Android上是有可能出现的。备注,然而jclass是一个类引用,还有这类必须是protected权限或者有更高的访问权限(例如public)时才会被NewGlobalRel获得。
如果你想在一个类被第一次加载,和类被回收和重新加载的时候,自动缓存和重新缓存他们的ID(MethodID和fieldID),正确的初始化方式就是添加
一段像下面一样的代码在需要被访问的类里面
/*
* We use a class initializer to allow the native code to cache some
* field offsets. This native function looks up and caches interesting
* class/field/method IDs. Throws on failure.
*/
private static native void nativeInit();
static {
nativeInit();
}
4.Local and Global References
传递给Native方法的参数以及绝大多数JNI函数的返回值都是局部引用,局部引用只在Native函数调用期间有效,当Native函数调用完毕后,局部引用将失效。如果想持有一个对象的全局引用,应使用NewGlobalRef,NewGlobalWeakRef, NewGlobalRef保证全局引用的全局有效性,直到显式调用了DeleteGlobalRef。一旦对某个对象调用了NewGlobalRef,其引用计数将增加,VM将不会对其执行垃圾回收,因此在必要的时刻,应该显式调用DeleteGlobalRef来释放被引用的对象,让其被VM回收。(这应用到Object所有子类,包括jclass,jstring,和jarray)
唯一拿到非局部引用的方式是通过NewGlobalRef或者NewWeakGlobalRef.
如果你想长时间持有一个引用,你必须用全局引用,NewGlobalReef 方法拿局部引用作为参数返回一个全局引用,全局引用一直有效直到你调用DeleteGlobalRef为止。
当要缓存一个从FindClass返回的jclass时,下面这段代码经常被用到
jclass localClass = env->FindClass("MyClass");
jclass globalClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
JNI中可以有多个引用指向同一个对象,如果需要测试两个引用是否指向的是同一个对象,应该使用IsSameObject方法,而不是“==”。
JNI数据类型中,jfieldID以及jmethodID不是引用类型,不能对这两个类型的数据使用NewGlobalRef;由GetStringUTFChars以及GetByteArrayElements返回的原始数据指针也不是Object类型,返回的指针可以在多个线程中使用,直到调用了对应的Release方法。
有一种特殊的情况你需要注意:如果你将一个Native线程通过AttachCurrentThread方法Attach到VM,则该Native线程中的Local Reference始终不会自动free掉(线程Detach时才会free掉Local Reference),因此这种情况下,你需要手动Delete所以你创建的Local Reference。通常,在一个Loop中创建的任何Local Reference也需要手动Delete,因为VM能够创建的Local Reference的数量是有限的。
5.UTF-8 and UTF-16 Strings
Java中Char类型与String类型使用UTF-16编码,而C代码中的Char以及String(字符数组)使用UTF-8编码。为解决这个问题JNI提供GetStringUTFChars函数用于返回UTF-8编码的String,但调用该方法将导致额外的内存分配和转换操作,执行速率不及GetStringChars。
当通过Get方法获取到String后不要忘记调用Release方法。String Get方法返回的C方式的指针jchar ,jbyte指向的是原始的数据而并非局部引用,因此在调用Release方法之前这些指针都是有效的,同时也以为这,在Native方法Return后VM不会自动释放指向的资源。
不要向NewStringUTF方法传递非UTF-8编码的数据。一个常见的错误是从文件或者网络读取数据然后直接传递给NewStringUTF。除非你非常清楚数据是ASCII格式的,否则你应当对其做适当的转换,NewStringUTF只接受UTF-8编码的字符串。
6.Exceptions
在异常已经发生的时候不能调用JNI方法,你的代码应该通过ExceptionCheck和ExceptionOccured方法判断是否有异常发生,如果异常发生你可以:返回、处理异常清除异常。在异常发生的时候可以调用的JNI方法如下:
- DeleteGlobalRef
- DeleteLocalRef
- leteWeakGlobalRef
- ExceptionCheck
- ExceptionClear
- ExceptionDescribe
- ExceptionOccurred
- MonitorExit
- PopLocalFrame
- PushLocalFrame
- Release\
7.Extended Checking
VM会对JNI调用做一些额外的运行时检查,包括:
- 数组:分配数组的长度为负值。
- 非法指针:传递非法的jarray/jclass/jobject/jstring指针到JNI函数。或者传递NULL指针给一个必须为非NULL的参数。
- 类名:传递非法的类名,正确的类名的写法应类似”java/lang/String”。
- DirectByteBuffer:传递非法的参数到NewDirectByteBuffer。
- 异常:在JNI Exception发生时,不做任何处理继续调用JNI方法。
- JNIEnv*s:在线程中错误的使用JNIEnv(每个线程都有不同的JNIEnv,不能公用)。
- jfieldID,jmethodID:使用和传递非法的或者不匹配的ID。
- 引用:Delete引用的时候调用的Delete方法不匹配。
- Release Mode:在调用Release方法的时候使用了错误了mode(0,JNI_COMMIT,JNI_ABORT)。
- 类型安全:Native方法返回值不匹配。
- UFT-8:传递了无效的UTF-8字符串到JNI方法。
启动JNI检查的方法
Emulator 默认已打开
ROOT过的设备
adb shell stop
adb shell setprop dalvik.vm.checkjni true
adb shell start
- 1
- 2
- 3
- 1
- 2
- 3
常规设备
adb shell setprop debug.checkjni 1
Jni Tips的更多相关文章
- android 官方文档 JNI TIPS
文章地址 http://developer.android.com/training/articles/perf-jni.html JNI Tips JNI is the Java Native I ...
- Android 性能优化(18)JNI优化:JNI Tips 提升性能技巧
JNI Tips 1.In this document JavaVM and JNIEnv Threads jclass, jmethodID, and jfieldID Local and Glob ...
- NDK(5) Android JNI官方综合教程[JavaVM and JNIEnv,Threads ,jclass, jmethodID, and jfieldID,UTF-8 and UTF-16 Strings,Exceptions,Native Libraries等等]
JNI Tips In this document JavaVM and JNIEnv Threads jclass, jmethodID, and jfieldID Local and Global ...
- [Android Webkit]JNI基础及Java层与C++层的交互
1. JNI 注册 1.1. JNI的基础结构 JAVA == JNI == Native Code JNI(Java Native Interface)是Java与Native ...
- Android JNI(NDK)开发总结
早就知道Java有个jni可以调用本地化代码,一直没有动力去研究它,现在公司想通过在Android中调用本地化代码来申请较多的内存以突破Android对单个进程的内存限制,这确实是可行的:我的Nexu ...
- Android应用程序性能优化Tips
对于我们设计的应用需要做到以下特征:build an app that's smooth, responsive(反应敏捷), and uses as little battery as possib ...
- JNI 引用问题梳理(转)
局部引用: JNI 函数内部创建的 jobject 对象及其子类( jclass . jstring . jarray 等) 对象都是局部引用,它们在 JNI 函数返回后无效: 一般情况下,我们应该依 ...
- Android深入理解JNI(二)类型转换、方法签名和JNIEnv
相关文章 Android深入理解JNI系列 前言 上一篇文章介绍了JNI的基本原理和注册,这一篇接着带领大家来学习JNI的数据类型转换.方法签名和JNIEnv. 1.数据类型的转换 首先给出上一篇文章 ...
- 性能优化小Tips
Performance Tips 这篇文章主要是介绍了一些小细节的优化技巧,当这些小技巧综合使用起来的时候,对于整个App的性能提升还是有作用的,只是不能较大幅度的提升性能而已.选择合适的算法与数据结 ...
随机推荐
- Android本地化资源目录详解
我们可以设想,有两个不同分辨率的手机(320*480和480*800)要使用一些图像资源,为了使图像不失真,就需要为不同分辨率的手机指定不同的图像,为此就需要建立不同的资源目录. 在res目录中建立了 ...
- 前端MVC学习笔记(二)——AngularJS验证、过滤器、指令
一.验证 angularJS中提供了许多的验证指令,可以轻松的实现验证,只需要在表单元素上添加相应的ng属性,常见的如下所示: <input Type="text" ng-m ...
- 浅谈CSS3 box-reflect 属性
今天说一下 CSS3的box-reflect属性,也就是倒影特效. 语法: box-reflect:包括3个值. 1. direction 定义方向,取值包括 above . below . left ...
- (十一)if...else&for循环&while循环
----------------------------------if else------------------------------1.最基本的if语句:if name =="Al ...
- Cocos2d-x 3.x事件分发机制总结
在2.x中处理事件需要用到委托代理(delegate),相信学过2.x的触摸事件的同学,都知道创建和移除的流程十分繁琐.而在3.x中由于加入了C++11的特性,而对事件的分发机制通过事件分发器Even ...
- JavaWeb开发中的乱码问题
一,获取系统平台的默认编码 获取系统平台的默认编码: String encoding=System.getProperty("file.encoding"); 注:至于UTF-8编 ...
- C++引用(&)详解
C++引用详解 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样. 引用的声明方法:类型标识符 &引用名=目标变量名: 如下:定义引用ra,它是变量a的引 ...
- wife信号如何传播
方法一:像哈利波特一样穿墙而出 无论是wife信号还是广播信号本质上都属于电磁波.x光穿透力强所以可以穿透人体给体内照相,但是wife信号作为电磁波虽然也可以穿透墙而过,但是他的穿透能力实在是太弱了. ...
- 关于Container With Most Water的求解
Container With Most Water 哎,最近心情烦躁,想在leetcode找找感觉,就看到了这题. 然而,看了题目半天,硬是没看懂,于是乎就百度了下,怕看到解题方法,就略看了下摘要,以 ...
- IMAX公司CEO:进军VR产业,打造VR体验中心
591ARVR资讯网www.591arvr.com报道近日,<财富>杂志采访了IMAX首席执行官理查德·葛尔方(Richar Gelfond),后者谈了谈虚拟现实和IMAX正在打造的VR影 ...