Android的JNI调用(二)
Android Studio 2.3在native下已经有了代码提示功能,按照提示下载相应组件就可以debug native代码。
一、Java调用JNI与JNI调用Java
1.1 C调用Java
Java可以调用native层的C代码,同理C代码也可以调用Java代码,方法如下。
- package com.example.jiayayao.myapplication;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.widget.TextView;
- public class MainActivity extends AppCompatActivity {
- JavaClass mClass = new JavaClass();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // Example of a call to a native method
- TextView tv = (TextView) findViewById(R.id.sample_text);
- tv.setText(stringFromJNI());
- }
- private String instanceMethod() {
- return "instance method";
- }
- private static String staticMethod() {
- return "static method";
- }
- /**
- * A native method that is implemented by the 'native-lib' native library,
- * which is packaged with this application.
- */
- public native String stringFromJNI();
- // Used to load the 'native-lib' library on application startup.
- static {
- System.loadLibrary("native-lib");
- }
- }
- #include <jni.h>
- #include <string>
- extern "C"
- JNIEXPORT jstring JNICALL
- Java_com_example_jiayayao_myapplication_MainActivity_stringFromJNI(
- JNIEnv* env,
- jobject obj) {
- // 通过对象引用获得类
- jclass clazz;
- clazz = env->GetObjectClass(obj);
- // 获得方法ID
- jmethodID instanceMethodId;
- instanceMethodId = env->GetMethodID(clazz, "instanceMethod", "()Ljava/lang/String;");
- // 执行方法
- jstring instanceMethodResult;
- instanceMethodResult = (jstring)(env->CallObjectMethod(obj, instanceMethodId));
- // 获得静态方法ID
- jmethodID staticMethodId;
- staticMethodId = env->GetStaticMethodID(clazz, "staticMethod", "()Ljava/lang/String;");
- // 执行静态方法
- jstring staticMethodResult;
- staticMethodResult = (jstring)(env->CallStaticObjectMethod(clazz, staticMethodId));
- std::string hello = "Hello from C++";
- return env->NewStringUTF(hello.c_str());
- }
但是Java和native代码之间的转换是代价较大的操作。应尽量最小化这种转换。
1.2 域和方法描述符
获取域ID和方法ID均分别需要域描述符和方法描述符。JDK提供命令行方式下的Java类文件反汇编程序成为javap,该工具可以从编译的类文件中解压缩域和方法描述符。javap格式及示例如下:
- C:\Users\jiayayao>javap -classpath D:\Work\MyApplication\app\build\intermediates\classes\debug\com\example\jiayayao\myapplication -p -s MainActivity
- Compiled from "MainActivity.java"
- public class com.example.jiayayao.myapplication.MainActivity extends android.support.v7.app.AppCompatActivity {
- com.example.jiayayao.myapplication.JavaClass mClass;
- descriptor: Lcom/example/jiayayao/myapplication/JavaClass;
- public com.example.jiayayao.myapplication.MainActivity();
- descriptor: ()V
- protected void onCreate(android.os.Bundle);
- descriptor: (Landroid/os/Bundle;)V
- private java.lang.String instanceMethod();
- descriptor: ()Ljava/lang/String;
- private static java.lang.String staticMethod();
- descriptor: ()Ljava/lang/String;
- public native java.lang.String stringFromJNI();
- descriptor: ()Ljava/lang/String;
- static {};
- descriptor: ()V
- }
native代码注意异常处理,native代码crash会发送SIG 33信号,该信号是bionic 库栈回溯使用的。
- // POSIX timers use __SIGRTMIN + 0.
- // libbacktrace uses __SIGRTMIN + 1.
- // libcore uses __SIGRTMIN + 2.
- #define __SIGRTMIN 32
二、局部和全局引用
2.1 局部引用
大多数JNI函数返回局部引用。局部引用不能在后续的调用中被缓存及重用,主要因为他们的使用期限仅限于原生方法,一旦原生方法返回,局部引用即被释放。例如,FindClass函数返回一个局部引用,当原生方法返回时,它被自动释放,也可以用DeleteLocalRef函数显示释放原生代码。
- jclass clazz2;
- clazz2 = env->FindClass("java/lang/String");
- ......
- env->DeleteLocalRef(clazz2);
根据JNI的规范,虚拟机应该允许native代码创建最少16个局部引用。在单个方法调用时进行多个内存密集型操作的最佳实践是删除未用的局部引用。如果不可能,native可以在使用之前用EnsureLocalCapacity方法请求更多的局部引用槽。
这里需要注意的是,原来native代码中创建线程并attach出的JNIEnv不会自动释放局部引用,知道DetachCurrentThread被调用,如果这一期间的局部调用不会太多,则相安无事,但是代码中涉及到循环不停的进行JNI调用的话,一会就超额 了,所以从代码的严谨性的角度来说,局部引用建议手动释放。
2.2 全局引用
全局引用在原生方法的后续调用过程依然有效,除非它们被原生代码显示释放。
- // globalClazz应该要保存,以便其他native函数使用
- jclass globalClazz;
- globalClazz = (jclass) env->NewGlobalRef(clazz2);
- ......
- env->DeleteGlobalRef(globalClazz);
2.3 弱全局引用
全局引用的另一种类型是弱全局引用。与全局引用一样,弱全局引用在原生方法的后续调用过程中依然有效。与全局引用不同,弱全局引用并不组织潜在的对象被垃圾收回。因此在使用之前,要使用IsSameObject函数检验其是否仍然指向活动的类实例,不再赘述。
三、多线程
只有原生方法执行期间及正在执行原生方法的线程环境下局部引用是有效的,局部引用不能在多线程间共享,只有全局引用可以被多个线程共享。被传递给每个原生方法的JNIEnv接口指针在于方法调用相关的线程中也是有效的,它不能被其他线程缓存或使用。
3.1 同步
JNI的监视器允许原生代码利用Java对象同步,虚拟机保证存取监视器的线程能够安全之星,而其他线程等待监视器对象变成可用状态。
Java代码可以使用synchronize关键字对代码块进行同步,native利用JNI的监视器方法进行同步的代码块如下:
- if(JNI_OK == env->MonitorEnter(obj)){
- /*error happens*/
- }
- /* synchronize program block*/
- if(JNI_OK == env->MonitorExit(obj)) {
- /*error happens*/
- }
为了实现native线程与Java应用程序的通信,JNI提供了AttachCurrentThread接口,以便将原生线程附着到虚拟机上。
3.2 关于JNIEnv
JNIEnv是一个线程相关的,也就是说线程A有个JNIEnv,线程B有个JNIEnv。由于线程相关不能在B线程中去访问线程A的JNIEnv结构体。但是全进程只有一个JavaVM对象,所以可以保存并且在任何地方使用都没有问题。调用JavaVM的AttachCurrentThread函数,就可以得到这个线程的JNIEnv结构体。这样就可以在后台线程中回调Java函数。在后台线程退出前,需要调用JavaVM的DetachCurrentThread函数来释放对应的资源。
Android的JNI调用(二)的更多相关文章
- [置顶] android利用jni调用第三方库——第三篇——编写库android程序整合第三方库libhello.so到自己的库libhelloword.so
0:前言: 在第二篇中,我们主要介绍了丙方android公司利用乙方C++公司给的动态库,直接调用库中的方法,但是这样方式受限于: 乙方C++公司开发的动态库是否符合jni的规范,如果不规范,则不能直 ...
- [置顶] android利用jni调用第三方库——第二篇——编写库android程序直接调用第三方库libhello.so
0:前言 1:本文主要作为丙方android公司的身份来写 2:作者有不对的地方,请指出,谢谢 [第一篇:android利用jni调用第三方库——编写库libhello.so] [第二篇:androi ...
- Android通过JNI调用驱动程序(完全解析实例)
要达到的目的:android系统中,用JAVA写界面程序,调用jni中间库提供的接口,去操作某个驱动节点,实现read,writer ioctl等操作!这对底层驱动开发人员是很重要的一个调试通道,也是 ...
- 【转】Android通过JNI调用驱动程序(完全解析实例)
原文网址:http://blog.csdn.net/ok138ok/article/details/6560875 要达到的目的:android系统中,用JAVA写界面程序,调用jni中间库提供的接口 ...
- Android的JNI调用(一)
Android提供NDK开发包来提供Android平台的C++开发,用来扩展Android SDK的功能.主要包括Android NDK构建系统和JNI实现与原生代码通信两部分. 一.Android ...
- android camera jni调用
http://www.mamicode.com/info-detail-1002139.html how to compile library of native camera for androi ...
- (AIDE)Android Eclipse JNI 调用 .so文件加载问题
背景:对于Android工程 Eclipse里编译好的.so文件放到 libs\armeabi下以后, 这样.so文件就可以打包到apk文件里,在apk装到手机上以后 在libs\armeabi下的. ...
- Android于JNI调用列出的程序
1.安装和下载cygwin,下载Android NDK: 2.于ndk工程JNI接口设计: 3.采用C/C++实现本地方法. 4.JNI生成动态链接库.so档: 5.动态链接库副本javaprojec ...
- android studio jni调用入门
一.开发环境配置: 1.Android Studio 2.3.3 2.android-ndk-r14b-windows-x86_64 二.创建项目 1.新建android项目 2.新建文件 3.编译生 ...
随机推荐
- bat批处理中如何获取前一天日期
网上找了好久在批处理中生成前一日期的代码段 但网上找到的代码对 每个月的1号和每年的1号计算前一日期时,总会报错,然后要加很多的逻辑判断 想了想,可以用.net写个EXE程序,用.net实现获取前一日 ...
- C++11:实用特性
今天逛cplusplus.com发现C++还真多了不少方便使用的特性,先了解些最常用的 初始化列表 vector<,,,}); vector<pair<int, int> &g ...
- 用css动画写一个下红包雨的效果
红包雨的功能相信大家都做过,不过一般都是用js计算的,闲着无聊用css的样式写了类似的,主要用的是css的transform和animation结合.大概代码逻辑: @keyframes startH ...
- KDTree(Bzoj2648: SJY摆棋子)
题面 传送门 KDTree 大概就是一个分割\(k\)维空间的数据结构,二叉树 建立:每层选取一维为关键字,把中间的点拿出来,递归左右,有个\(STL\)函数nth_element可以用一下 维护:维 ...
- C语言——二叉排序树
二叉排序树是一种实现动态查找的树表,又称二叉查找树. 二叉排序树的性质: 1. 若它的左子树不为空,则左子树上所有节点的键值均小于它的根节点键值 2. 若它的右子树不为空,则右子树上所有节点的键值均大 ...
- 重温C语言(1)----计算算术表达式的值
<C程序设计语言>练习题 5-10 编写程序 expr,计算从命令行输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示.例如,命令 expr 2 3 4 + * 计算表达式 ...
- 自动生成了一本ES6的书
:1.在windows下配置hadoop的环境变量2.拷贝debug工具(winutils.exe)到hadoop目录中的bin目录,注意winutils.exe的版本要 ...
- 在Oracle/SQL Service中通过Function返回Table
本函数用途:返回一个Table 在Oracle中实现,范例: --在Types中: create or replace type objTable as object ( s_usercode var ...
- selenium元素定位方法
一.如何找到页面元素 Webdriver的findElement方法可以用来找到页面的某个元素,最常用的方法是用id和name查找.下面介绍几种比较常用的方法. 1.1By ID 假设页面写成这样:i ...