05 子线程findclass

JNI子线程FindClass失败

 

1、在C语言里创建子线程

 

 在进行jni开发时,Java调用C语言一般都处于主线程中的,但是使用JNI开发,很多情况都是需要开启子线程的(毕竟不能阻塞主线程),那么如何开启子线程尼?很简单,代码如下:

 

void void *th_fun(void *arg) {} //是子线程的回调函数

pthread_create(&tid, NULL/*很少用到*/, th_fun/*子线程回调*/, (void *) "no1"/*传递给子线程的参数*/);

 

 

2、在子线程使用env

 

 有时候在子线程会去调用Java方法,那么如何调用尼?一般我们都会通过env->FIndClass来调用,但是如何在子线程回调函数里拿到env尼?将env设为全局引用,这是一个解决方案,但是env本就是与线程相关的,如果设为全局引用给其他线程调用,这样就搞混乱了,所以不好。那么如何解决尼?其实我们可以通过JavaVM来解决,JavaVM代表的是Java虚拟机,所有工作都是从JavaVM开始的,每个Java程序代表一个JavaVM,Android里每个Android程序都的JavaVM都是一样的。解决方案如下:

 

static JavaVM *javaVM;

 

//动态库加载时会执行

//兼容Android SDK 2.2之后,2.2没有这个函数

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {

LOGI("%s", "JNI_OnLoad");

javaVM = vm;

return JNI_VERSION_1_4;

}

void *th_fun(void *arg) {

JNIEnv *env = NULL;

int isAttacked = 0;

int status = (*javaVM)->GetEnv(javaVM, (void **) &env, JNI_VERSION_1_4);

if (status < 0) {

isAttacked = 1;

(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);

}

}

 

3、在子线程FindClass失败

 

 有时候会在子线程去调用Java类,但是在我们创建的子线程(通过pthread_create创建)中调用FindClass查找非系统类时会失败(查找系统类不会失败),返回值为NULL,为什么尼?这是因为通过AttachCurrentThread附加到虚拟机的线程在查找类时只会通过系统类加载器进行查找,不会通过应用类加载器进行查找,因此可以加载系统类,但是不能加载非系统类,如自己在java层定义的类会返回NULL。

 那么如何解决尼?主要有以下两个方案

 

获取classLoader,通过调用classLoader的loadClass来加载自定义类。适合自定义类比较多的情况

在主线程创建一个全局的自定义类引用。适合自定义类比较少的情况

#include <jni.h>

#include <pthread.h>

#include <android/log.h>

#include <stdio.h>

#include <unistd.h>

 

#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"dadou",FORMAT,##__VA_ARGS__);

#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"dadou",FORMAT,##__VA_ARGS__);

 

 

static JavaVM *javaVM;

static jobject class_loader_obj_ = NULL;

static jmethodID find_class_mid_ = NULL;

static jclass global_ref = NULL;

 

//动态库加载时会执行

//兼容Android SDK 2.2之后,2.2没有这个函数

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {

LOGI("%s", "JNI_OnLoad");

javaVM = vm;

LOGI("a=%d,b=%d", vm == NULL, javaVM == NULL);

//--------------------------------------------方案一--------------------------------------------

// JNIEnv *env = NULL;

// int status = (*javaVM)->GetEnv(javaVM, (void **) &env, JNI_VERSION_1_4);

// if (status == JNI_OK) {//我认为最好在JNI_OnLoad里拿到classLoader的全局引用

// jclass classLoaderClass = (*env)->FindClass(env, "java/lang/ClassLoader");

// jclass adapterClass = (*env)->FindClass(env, "com/example/thread/UUIDUtils");

// if (adapterClass) {

// jmethodID getClassLoader = (*env)->GetStaticMethodID(env, adapterClass,

// "getClassLoader",

// "()Ljava/lang/ClassLoader;");

// jobject obj = (*env)->CallStaticObjectMethod(env, adapterClass, getClassLoader);

// class_loader_obj_ = (*env)->NewGlobalRef(env, obj);

// find_class_mid_ = (*env)->GetMethodID(env, classLoaderClass, "loadClass",

// "(Ljava/lang/String;)Ljava/lang/Class;");

// (*env)->DeleteLocalRef(env, classLoaderClass);

// (*env)->DeleteLocalRef(env, adapterClass);

// (*env)->DeleteLocalRef(env, obj);

// }

// }

//----------------------------------------------------------------------------------------------

return JNI_VERSION_1_4;

}

 

//子线程的回调

/**

* 在子线程中,不能通过env->FindClass来获取自定义类,(*env)->FindClass(env, "com/example/thread/UUIDUtils");返回NULL,

* (*env)->FindClass(env,"java/lang/String");能够正确的返回

* 解决方案一:获取classLoader,通过调用classLoader的loadClass来加载自定义类。适合自定义类比较多的情况

* 解决方案二:在主线程创建一个全局的自定义类引用。适合自定义类比较少的情况

* @param arg

* @return

*/

void *th_fun(void *arg) {

JNIEnv *env = NULL;

int isAttacked = 0;

int status = (*javaVM)->GetEnv(javaVM, (void **) &env, JNI_VERSION_1_4);

if (status < 0) {

isAttacked = 1;

(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);

}

jclass clazz = NULL;

//--------------------------------------------方案一--------------------------------------------

// jstring class_name = (*env)->NewStringUTF(env, "com/example/thread/UUIDUtils");

// clazz = (*env)->CallObjectMethod(env, class_loader_obj_, find_class_mid_,

// class_name);

// (*env)->DeleteLocalRef(env, class_name);

// if (clazz != NULL) {

// jmethodID get_mid = (*env)->GetStaticMethodID(env, clazz, "get",

// "()Ljava/lang/String;");

// jobject uuid = (*env)->CallStaticObjectMethod(env, clazz, get_mid);

// char *uuid_cstr = (char *) (*env)->GetStringUTFChars(env, uuid, NULL);

// LOGI("uuid : %s", uuid_cstr);

// (*env)->ReleaseStringUTFChars(env, uuid, uuid_cstr);

// }

//----------------------------------------------------------------------------------------------

//--------------------------------------------方案二--------------------------------------------

if (global_ref != NULL) {

jmethodID get_mid = (*env)->GetStaticMethodID(env, global_ref, "get",

"()Ljava/lang/String;");

jobject uuid = (*env)->CallStaticObjectMethod(env, global_ref, get_mid);

char *uuid_cstr = (char *) (*env)->GetStringUTFChars(env, uuid, NULL);

LOGI("uuid : %s", uuid_cstr);

(*env)->ReleaseStringUTFChars(env, uuid, uuid_cstr);

}

//----------------------------------------------------------------------------------------------

 

char *no = (char *) arg;

int i = 0;

for (i = 0; i < 5; i++) {

LOGI("thread %s, i:%d", no, i);

if (i == 4) {

if (class_loader_obj_ != NULL) {

(*env)->DeleteGlobalRef(env, class_loader_obj_);

}

//采用方案二时需要释放全局引用

//---------------释放---------

if (global_ref != NULL) {

LOGI("%s", "开始释放全局引用")

(*env)->DeleteGlobalRef(env, global_ref);

}

//----------------------------

 

//下面的函数必须最后执行,在后面再使用env会报错

if (isAttacked == 1) {

//解除关联

(*javaVM)->DetachCurrentThread(javaVM);//必须在离开当前线程之前执行

}

pthread_exit((void *) 0);

}

sleep(1);

}

 

}

 

 

//JavaVM 代表的是Java虚拟机,所有工作都是从JavaVM开始的,每个Java程序代表一个JavaVM,Android里每个Android程序都的JavaVM都是一样的

//可以通过JavaVM获取到每个线程关联的JNIEnv

 

 

//如何获取JavaVM?

//1.在JNI_OnLoad函数中获取

//2.(*env)->GetJavaVM(env,&javaVM);

//每个线程都有独立的JNIEnv

JNIEXPORT jstring JNICALL Java_com_example_thread_MainActivity_stringFromJNI(

JNIEnv *env, jobject /* this */ object) {

char str[] = "Hello from C";

jclass clazz = (*env)->FindClass(env, "com/example/thread/UUIDUtils");

global_ref = (*env)->NewGlobalRef(env, clazz);

pthread_t tid;//子线程id

//创建一个子线程

pthread_create(&tid, NULL/*很少用到*/, th_fun/*子线程回调*/, (void *) "no1"/*传递给子线程的参数*/);

return (*env)->NewStringUTF(env, str);

}

 

 

 下面就是Java方法

 

public class UUIDUtils {

public static ClassLoader getClassLoader() {

return UUIDUtils.class.getClassLoader();

}

public static String get(){

return UUID.randomUUID().toString();

}

}

 

原文:https://blog.csdn.net/lmh_19941113/article/details/85048184

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值