inflect java_在native线程利用JNI 反射自定义类
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++回調的時候直接使用該全局類或函數方法即可
這樣類就反射出來了
總結
以上是生活随笔為你收集整理的inflect java_在native线程利用JNI 反射自定义类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android自动隐藏虚拟键,Andro
- 下一篇: 字节跳动python后端_【字节跳动】[