JNI中AttachCurrentThread和DetachCurrentThread的问题
在《Java与CC++交互JNI编程》中有讲过AttachCurrentThread和DetachCurrentThread的使用。
我们知道在jni中我们可以使用pthread或者std::thread创建线程。因为线程并不是从Java环境创建的,所以这时候创建出的线程是没有JNIEnv的。如果需要使用JNIEnv,可以调用JavaVM的AttachCurrentThread将当前线程附加到虚拟机。
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args);
AttachCurrentThread可以调用多次,第一次会附加当前线程到虚拟机并返回JNIEnv,之后再调用的时候因为当前线程已经被附加到虚拟机上了,所以就不需要重复附加了,仅仅只返回JNIEnv即可,作用相当于GetEnv。
需要注意的是,在线程退出之前我们必须要调用DetachCurrentThread从虚拟机分离当前线程,,不然会造成内存泄露,线程也不会退出。对于native层创建出来的线程,在调用AttachCurrentThread的时候会创建本地引用表,在调用DetachCurrentThread的时候会释放本地引用表。
但是一般我们并不会频繁的调用AttachCurrentThread/DetachCurrentThread,这样效率很低。一般我们在线程的入口函数调用一次AttachCurrentThread,在线程入口函数退出之前调用一次DetachCurrentThread即可。所以一定要手动释放每个本地引用。不然本地引用越来越多,很容易超出最大限制。
下面这个例子很好的演示了AttachCurrentThread/DetachCurrentThread的用法:
#include <jni.h>
#include <pthread.h>
#include <android/log.h>
extern JavaVM *g_vm;
JNIEnv* getEnv();
void* __start_routine(void*) {
JNIEnv *env1, *env2, *env3, *env4, *env5;
int ret;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_4;
args.name = "pthread-test";//给线程起个名字吧,这样在调试或者崩溃的时候能显示出名字,而不是thead-1,thread-2这样的名字。
args.group = NULL;//java.lang.ThreadGroup的全局引用,作用你懂的。
//在调用AttachCurrentThread以前,是没有java环境的,所以GetEnv返回的JNIEnv是NULL
g_vm->GetEnv((void**)&env1,JNI_VERSION_1_4);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "before AttachCurrentThread env is:%p", env1);
//调用AttachCurrentThread,将当前线程附加到虚拟机,附加成功后,将会返回JNIEnv
ret = g_vm->AttachCurrentThread(&env2, &args);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "do AttachCurrentThread env is:%p, ret=%d", env2, ret);
//在调用AttachCurrentThread以后,GetEnv返回了正确的JNIEnv
g_vm->GetEnv((void**)&env3,JNI_VERSION_1_4);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "after AttachCurrentThread env is:%p", env3);
//再次调用AttachCurrentThread,直接返回JNIEnv,作用相当于GetEnv
ret = g_vm->AttachCurrentThread(&env4, NULL);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "retry AttachCurrentThread env is:%p, ret=%d", env4, ret);
//从虚拟机分离线程
g_vm->DetachCurrentThread();
//在调用DetachCurrentThread以后,GetEnv返回NULL
g_vm->GetEnv((void**)&env5,JNI_VERSION_1_4);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "after DetachCurrentThread env is:%p", env5);
return NULL;
}
extern "C" JNIEXPORT void Java_com_example_threadtest_PThread_start(JNIEnv *env, jclass clazz) {
pthread_t thread;
pthread_create(&thread, NULL, __start_routine, NULL);
}
深入浅出Android NDK之在jni中使用线程
关于JNI开发的一些建议
JNI中AttachCurrentThread和DetachCurrentThread的问题的更多相关文章
- ZT ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01
ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01 分类: 嵌入式 android framework 里java调用native,使用JNI机制,ja ...
- Java Native Interface 六JNI中的异常
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 在这里只讨论调用JNI方法可能会出现的异常, ...
- Java Native Interfce三在JNI中使用Java类的普通方法与变量
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 前面我们学习了如何在JNI中通过参数来使用J ...
- Java Native Interface 二 JNI中对Java基本类型和引用类型的处理
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 Java编程里会使用到两种类型:基本类型(如 ...
- JNI中C调用Java方法
背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...
- [Android Pro] Android studio jni中调用Log输出调试信息
reference to : http://www.linuxidc.com/Linux/2014-02/96341.htm Android 开发中,java 可以方便的使用调试信息Log.i, Lo ...
- Jni中C++和Java的参数传递 参数对照
Jni中C++和Java的参数传递 如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用V ...
- Jni中C++和Java的参数传递
Jni中C++和Java的参数传递 如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用V ...
- Base64编解码Android和ios的例子,补充JNI中的例子
1.在Android中java层提供了工具类:android.util.Base64; 里面都是静态方法,方便直接使用: 使用方法如下: // Base64 编码: byte [] encode = ...
- android学习笔记----JNI中的c控制java
面向对象的底层实现 java作为面向对象高级语言,可对现实世界进行建模.和面向过程不同的是面向对象软件的编写不是流程的堆积,而是对业务逻辑的多视角分解和分类.其过程大致为: 1).将知识分解 ...
随机推荐
- setjmp/longjmp使用问题
setjmp/longjmp开启编译优化后导致出现无法正常使用
- 聚石塔的K8s 容器服务使用注意事项,坑的总结
1. 首先聚石塔是不能使用 8080 端口的,会审核不通过. 2.然而,容器服务默认的却是8080,最彻底的解决方法就是修改成80,注意3个地方: 以上3个地方要严重留意,缺一不可,已经踩了2次坑了, ...
- npm i -D和-s及-g以及--save的那些事
i 是 install 的简写 -S 就是 --save 的简写 -D 就是 --save-dev 的简写 npm i module_name -S = > npm install modu ...
- Linux通过fdisk或者parted进行磁盘分区,然后格式化和挂载磁盘
磁盘分区是Linux的常用命令,其中fdisk和parted命令最为常用.但是当磁盘大于2T时,fdisk只能分到2T. 比如4T的磁盘,fdisk只能分2T的主分区出来,parted可以直接分成4T ...
- DecisionTreeClassifier&DecisionTreeClassRegression
DecisionTreeClassifier from sklearn.datasets import load_wine # 红酒数据集 from sklearn.tree import Decis ...
- NC19782 Tree
题目链接 题目 题目描述 修修去年种下了一棵树,现在它已经有n个结点了. 修修非常擅长数数,他很快就数出了包含每个点的连通点集的数量. 澜澜也想知道答案,但他不会数数,于是他把问题交给了你. 输入描述 ...
- NC17872 CSL的校园卡
题目链接 题目 题目描述 今天是阳光明媚,晴空万里的一天,CSL早早就高兴地起床走出寝室到校园里转悠. 但是,等到他回来的时候,发现他的校园卡不见了,于是他需要走遍校园寻找它的校园卡.CSL想要尽快地 ...
- SSD 表项管理概述(一)——L1、L2、L3
分类 名称 说明 映射表相关 L1 Table 记录每个4KB用户数据在SSD上的存放物理地址: L2 Table 记录每个sub L1 Table在SSD上的存放物理地址: L3 Table 记录每 ...
- C++ 多线程的错误和如何避免(8)
不要重复获取同一个锁 问题:在获得一个锁并且没有释放该锁的前提下,再次尝试获取该锁会报错. 比如, #include <iostream> #include <thread> ...
- win32-localtime的使用
下面的例子用于反映本地系统的日期格式变化 // locale test #include <stdio.h> #include <locale.h> #include < ...