要在jni代码的线程中调用java代码的方法,必须把当前线程连接到VM中,获取到一个[JNIEnv*].
A JNI interface pointer (JNIEnv*) is passed as an argument for each native function mapped to a Java method, allowing for interaction with the JNI environment within the native method.This JNI interface pointer can be stored, but remains valid only in the current thread. Other threads must first call AttachCurrentThread()to attach themselves to the VM and obtain a JNI interface pointer. Once attached, a native thread works like a regular Java thread running within a native method. The native thread remains attached to the VM until it callsDetachCurrentThread() to detach itself.[3]
JNI接口指针(JNIEnv*)作为映射到Java方法的每个本地函数的参数传递,允许与本机方法内的JNI环境进行交互。可以存储此JNI接口指针,但仅在当前线程中保持有效。其他线程必须首先调用AttachCurrentThread()将自己连接到VM并获取JNI接口指针。连接后,本机线程的工作方式类似于在本机方法中运行的常规Java线程。本机线程将保持连接到VM,直到它调用DeachCurrentThread()以分离自身。[3]
即:当在一个线程里面调用AttachCurrentThread后,如果不需要用的时候一定要DetachCurrentThread,否则线程无法正常退出。
注意点
1 保存全局 JavaVM
2 保存全局 jobject
3 退出线程必须调用 DetachCurrentThread
相关函数
【GetEnv】
返回结果为 JNI_OK 表示当前线程已经绑定到 VM 中 ,如果未绑定再调用 AttachCurrentThread
【NewStringUTF】
该方法可以不调用 DeleteLocalRef, openJdk中的例程使用也不需要释放,不过释放相对较好
#include "jni_main.h"
#include <pthread.h>
#include <stdio.h>
static const char* ClassPathName = "com/example/hellojni/JniClass";
static JavaVM *ms2_vm = NULL;
static jobject g_obj;
static JNINativeMethod JnidecgMethods[] = {
{"jni_debug_test1","()V",(void *) jni_debug_test1},
};
bool get_env(JNIEnv ** env) {
int status = ms2_vm->GetEnv((void**) env, JNI_VERSION_1_4);
if (status != JNI_OK) {
status = ms2_vm->AttachCurrentThread(env, NULL);
if(status != JNI_OK){
ehome_printf("[%s]FAILED\n", __FUNCTION__);
return false;
}
ehome_printf("[%s]SUCCESS\n", __FUNCTION__);
}else{
ehome_printf("[%s]Attach aready\n", __FUNCTION__);
}
return true;
}
void release_env(void) {
JNIEnv *env ;
int status = ms2_vm->GetEnv((void**)&env, JNI_VERSION_1_4);
if (status == JNI_OK) {
ehome_printf("[%s]getpid=%d, gettid=%d\n", __FUNCTION__, getpid(),gettid());
ms2_vm->DetachCurrentThread();
}else{
ehome_printf("[%s]NEED NOT DETACH\n", __FUNCTION__);
}
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
jclass clazz = env->FindClass(ClassPathName);
if (clazz == NULL) {
ehome_printf("[%s]cannot find class '%s'\n", __FUNCTION__,
ClassPathName);
return -1;
}
if (env->RegisterNatives(clazz, JnidecgMethods,
sizeof(JnidecgMethods) / sizeof(JnidecgMethods[0])) < 0) {
ehome_printf("[%s]failed for '%s'\n", __FUNCTION__,
ClassPathName);
return (-1);
}
ms2_vm = vm;
return JNI_VERSION_1_4;
}
static void* thread_debug1(void* argv){
JNIEnv *env ;
if (!get_env(&env)) {
ehome_printf("[%s]get_env error!\n", __FUNCTION__);
return NULL;
}
ehome_printf("[%s]GetVersion=%d\n", __FUNCTION__, env->GetVersion());
jclass clazz = env->GetObjectClass(g_obj);
if (clazz == NULL) {
ehome_printf("[%s]unable to find class '%s'\n",
__FUNCTION__, ClassPathName);
release_env();
return NULL;
}
jmethodID CallBackid = env->GetMethodID(clazz,
"on_post_from_jni",
"(I)V");
for(int i=0; i<5; i++){
env->CallVoidMethod(g_obj, CallBackid, 1001+i);
sleep(1);
}
release_env();
return NULL;
}
static void* thread_debug2(void* argv){
JNIEnv *env ;
if (!get_env(&env)) {
ehome_printf("[%s]get_env error!\n", __FUNCTION__);
return NULL;
}
ehome_printf("[%s]GetVersion=%d\n", __FUNCTION__, env->GetVersion());
jclass clazz = env->GetObjectClass(g_obj);
if (clazz == NULL) {
ehome_printf("[%s]unable to find class '%s'\n",
__FUNCTION__, ClassPathName);
release_env();
return NULL;
}
jmethodID CallBackid = env->GetMethodID(clazz,
"on_post_from_jni",
"(I)V");
for(int i=0; i<5; i++){
env->CallVoidMethod(g_obj, CallBackid, 2001+i);
get_env(&env);
sleep(1);
}
release_env();
return NULL;
}
static void* thread_debug3(void* argv){
JNIEnv *env ;
if (!get_env(&env)) {
ehome_printf("[%s]get_env error!\n", __FUNCTION__);
return NULL;
}
ehome_printf("[%s]GetVersion=%d\n", __FUNCTION__, env->GetVersion());
//jclass clazz = env->GetObjectClass(g_obj);
jclass clazz = env->FindClass("android/util/Log");
if (clazz == NULL) {
ehome_printf("[%s]unable to find class '%s'\n",
__FUNCTION__, ClassPathName);
release_env();
return NULL;
}
jmethodID CallBackid = env->GetStaticMethodID(clazz,
"w",
"(Ljava/lang/String;Ljava/lang/String;)I");
jstring jstr1;
jstring jstr2;
char text[32] = {0};
for(int i=0; i<5; i++){
sprintf(text, "%s.%d", __FUNCTION__, i);
jstr1 = env->NewStringUTF("JNI_TAG");
jstr2 = env->NewStringUTF(text);
env->CallStaticIntMethod(clazz, CallBackid, jstr1, jstr2);
env->DeleteLocalRef(jstr1);
env->DeleteLocalRef(jstr2);
sleep(1);
}
release_env();
return NULL;
}
JNIEXPORT void JNICALL jni_debug_test1(JNIEnv *jenv, jobject thiz){
ehome_printf("[%s]start\n", __FUNCTION__);
g_obj = jenv->NewGlobalRef(thiz);
pthread_t tid1;
pthread_create(&tid1, NULL, thread_debug1, NULL);
pthread_t tid2;
pthread_create(&tid2, NULL, thread_debug2, NULL);
pthread_t tid3;
pthread_create(&tid3, NULL, thread_debug3, NULL);
}
头文件 jni_main.h
#ifndef _JNI_MAIN_H_
#define _JNI_MAIN_H_
#include <jni.h>
#include <android/log.h>
#include <unistd.h>
#define ehome_printf(format, ...) \
__android_log_print(ANDROID_LOG_DEBUG, "jni_debug", format, ##__VA_ARGS__)
#ifdef __cplusplus
extern "C"
{
#endif
JNIEXPORT void JNICALL jni_debug_test1(JNIEnv *jenv, jobject thiz);
#ifdef __cplusplus
}
#endif
#endif
运行结果
01-01 01:25:11.925: D/jni_debug(11967): [jni_debug_test1]start
01-01 01:25:11.929: D/jni_debug(11967): [get_env]SUCCESS
01-01 01:25:11.929: D/jni_debug(11967): [thread_debug2]GetVersion=65542
01-01 01:25:11.929: I/JniClass(11967): on_post_from_jni: number=2001
01-01 01:25:11.929: D/jni_debug(11967): [get_env]Attach aready
01-01 01:25:11.934: D/jni_debug(11967): [get_env]SUCCESS
01-01 01:25:11.934: D/jni_debug(11967): [thread_debug1]GetVersion=65542
01-01 01:25:11.934: I/JniClass(11967): on_post_from_jni: number=1001
01-01 01:25:11.938: D/jni_debug(11967): [get_env]SUCCESS
01-01 01:25:11.938: D/jni_debug(11967): [thread_debug3]GetVersion=65542
01-01 01:25:11.938: W/JNI_TAG(11967): thread_debug3.0
01-01 01:25:12.929: I/JniClass(11967): on_post_from_jni: number=2002
01-01 01:25:12.929: D/jni_debug(11967): [get_env]Attach aready
01-01 01:25:12.934: I/JniClass(11967): on_post_from_jni: number=1002
01-01 01:25:12.939: W/JNI_TAG(11967): thread_debug3.1
01-01 01:25:13.929: I/JniClass(11967): on_post_from_jni: number=2003
01-01 01:25:13.930: D/jni_debug(11967): [get_env]Attach aready
01-01 01:25:13.934: I/JniClass(11967): on_post_from_jni: number=1003
01-01 01:25:13.939: W/JNI_TAG(11967): thread_debug3.2
01-01 01:25:14.930: I/JniClass(11967): on_post_from_jni: number=2004
01-01 01:25:14.930: D/jni_debug(11967): [get_env]Attach aready
01-01 01:25:14.935: I/JniClass(11967): on_post_from_jni: number=1004
01-01 01:25:14.939: W/JNI_TAG(11967): thread_debug3.3
01-01 01:25:15.930: I/JniClass(11967): on_post_from_jni: number=2005
01-01 01:25:15.930: D/jni_debug(11967): [get_env]Attach aready
01-01 01:25:15.935: I/JniClass(11967): on_post_from_jni: number=1005
01-01 01:25:15.940: W/JNI_TAG(11967): thread_debug3.4
01-01 01:25:16.930: D/jni_debug(11967): [release_env]getpid=11967, gettid=11999
01-01 01:25:16.935: D/jni_debug(11967): [release_env]getpid=11967, gettid=11997
01-01 01:25:16.940: D/jni_debug(11967): [release_env]getpid=11967, gettid=12000