inflect java_在native线程利用JNI 反射自定义类

这篇博客详细介绍了在JNI中如何利用Java反射机制实现C++回调Java类的静态方法。通过JavaVM和JNIEnv接口,首先获取JNIEnv指针,然后找到目标类并获取静态方法ID,最终调用Java方法。同时,文章提到了在native线程中可能遇到的FindClass找不到自定义类的问题,并给出了解决方案,即在JNI_OnLoad中预先获取类并保存全局引用。
摘要由CSDN通过智能技术生成

NDK编程中遇到的一些细节问题,希望对大家有帮助

-----题记

在JNI中,有时候出于业务要求需要实现异步事件机制,例如网络通讯的收发

这时就会在C++中回调java类的方法,于是就会用到java反射机制

在JNI中,实现类反射主要用到以下几个方法:(本例以反射静态方法为例)

JavaVM             jint GetEnv(void **penv, jint version)

JavaVM             jint AttachCurrentThread(void **penv, void *args)

JNIEnv              jclass FindClass(const char *name)

JNIEnv              jmethodID GetStaticMethodID(jclass clazz, const char *name,  const char *sig)

JNIEnv            void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...)

JavaVM           jint DetachCurrentThread()

假设我们要反射的类是:

package com.genius.test;

public class InflectClass {

public static void test(int cmd,String value,String data){

}

}

那么C++中回调的JNI代码就是:

void testInflect(int cmd, const char* value, const char* data)

{

if (g_vm == NULL)

{

return ;

}

int status;

JNIEnv *env = NULL;

bool isAttach = false;

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

if(status != JNI_OK)

{

status = g_vm->AttachCurrentThread(&env, NULL);

if(status < 0) {

return;

}

isAttach = true;

}

jstring valueString = NULL;

jstring dataString = NULL;

jclass inflectClass = NULL;

jmethodID inflectMethod = NULL;

jclass inflectClass = env->FindClass("com/genius/test/InflectClass");

if (inflectClass == NULL)

{

return;

}

jmethodID inflectMethod= env->GetStaticMethodID(inflectClass, "test", "(ILjava/lang/String;Ljava/lang/String;)V");

if (inflectMethod == NULL)

{

return ;

}

valueString = env->NewStringUTF(value);

dataString = env->NewStringUTF(data);

env->CallStaticVoidMethod(inflectClass, inflectMethod, cmd, valueString, dataString);

end:

if (env->ExceptionOccurred())

{

env->ExceptionDescribe();

env->ExceptionClear();

}

if (isAttach)

{

g_vm->DetachCurrentThread();

}

env->DeleteLocalRef(dataString);

}

其中g_vm是全局的虚拟机实例,可以在

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);

函数中保存该实例

jint GetEnv(void **penv, jint version)是获取当前线程对应的JNI环境指针

在JNI中,多线程间JNIEnv 是不可以共享的,所以不能全局保存使用

获取是有可能失败的,比如当前是native线程

何为native线程?即在jni中开启的线程。

而java线程则是调用native方法的宿主线程,可以是主线程也可以是子线程

在获取失败时,调用jint AttachCurrentThread(void **penv, void *args)来将当前线程附加到虚拟机并获取JNI环境指针

之后就是通过

jclass FindClass(const char *name)

GetStaticMethodID(jclass clazz, const char *name,  const char *sig)

void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...)

来获取类方法并调用

GetStaticMethodID方法的第三个参数是函数签名

主要是c++中重载函数的存在使得函数名不能作为区别函数方法的唯一标示

这时就需要区别参数列表,获取函数签名的命令是javap

具体使用方法大家百度一下就知道了

最后如果有附加当前线程到虚拟机的话

需要调用DetachCurrentThread方法来释放线程

否则线程不能正常结束

温馨提示:

理论上通过上面几个步骤就可以反射到java层了

但实际操作起来会发现在native线程里执行FindClass的时候会找不到该自定义类(系统类可以)

具体原因不详,解决方案如下:

在JNI_OnLoad里获取该类对象并保存一个全局引用

JavaVM *g_vm = NULL;

jclass g_inflectClass = NULL;

jmethodID g_methodID = NULL;

void InitInflectClass(JavaVM* vm)

{

g_vm = vm;

JNIEnv *env = NULL;

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

if(status != JNI_OK)

{

return ;

}

jclass inflectClass = env->FindClass("com/genius/test/InflectClass");

if (inflectClass == NULL)

{

return ;

}

g_inflectClass = inflectClass;

g_methodID = env->GetStaticMethodID(inflectClass, "test", "(ILjava/lang/String;Ljava/lang/String;)V");

if (g_methodID == NULL)

{

return ;

}

}

直接把函数方法保存下来也可以

然后在C++回调的时候直接使用该全局类或函数方法即可

这样类就反射出来了

proud.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值