日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

android JNI层线程回调Java函数

發布時間:2024/4/15 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android JNI层线程回调Java函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天,簡單講講android的jni如何使用jni回調java函數。


之前,我寫了部分jni的博客,講的都是如何從android的java代碼調用jni的函數。最近,需要做一個新的功能,在jni的C函數里,需要開一個線程,不停回調java的函數。開始查了很多資料,最終是完成了效果。這里記錄一下。


需要調用的java的函數:

/** * * @param datas * @param dataSize * @param sync_code * @param frametype * @param frameno * @param channel * @param tv_sec * @param tv_msec */ public void decodeData(byte[] datas, int dataSize,int sync_code,int frametype,int frameno,int channel,int tv_sec,int tv_msec,int hStream) {}



二.在jni里首先進行回調java函數:

?1.首先定義一個保存變量的結構體。

//記錄類相關的信息 typedef struct ClassInfo {JavaVM *jvm; //保存java虛擬機,這是在新線程中能夠回調到java方法的最重要的參數. jobject obj; //保存java對象 jmethodID callbackMethodId; //保存methodID jmethodID receiveDeviceEventId; //保存methodID }ClassInfo; //定義一個全局的ClassInfo ClassInfo gClassInfo = {0};



2.對結構體的變量初始化。

JNIEXPORT jint JNICALL Java_com_p2p_protocol_Protocol_1APIs_initCallBack(JNIEnv *env, jobject obj){/** * 說明:jni層如果有多線程,實際上JNIEnv(jni環境變量)是不能夠在多線程中共用的, env只能在當前線程有效, * 但是JavaVM可以,JavaVMJava虛擬機,這個變量是進程可共用的.所以要想在其他線程中回調java方法,需要保存的是jvm. */ (*env).GetJavaVM(&gClassInfo.jvm); jclass cls = (*env).FindClass("com/p2p/protocol/Protocol_APIs"); if (NULL == cls) {LOGE("can't find jclass ProtocolCallBack"); return -1; }gClassInfo.callbackMethodId = (*env).GetMethodID(cls, "decodeData", "([BIIIIIIII)V"); if (NULL == gClassInfo.callbackMethodId) {LOGE("can't find method ProtocolCallBack from JniClass"); return -1; }gClassInfo.receiveDeviceEventId = (*env).GetMethodID(cls, "receiveDeviceEvent", "(II)V"); /** * 說明:為了能夠在其它線程得到java的對象,必須要instance轉化為全局對象,這樣在其它線程才能得到當前java對象的索引. * 否則在其它線程要用到當前java對象時,會出現無效引用的錯誤. */ gClassInfo.obj = (*env).NewGlobalRef(obj); if (NULL == gClassInfo.obj) {LOGE("can't find jobject"); return -1; }//調用decodeData方法 //env->CallVoidMethod(gClassInfo.obj,gClassInfo.callbackMethodId,NULL,10); return 0; }


說明一下初始化的內容,首先通過GetJavaVM獲取到java虛擬機,然后通過FindClass獲取調用jni的類,這里必須注意一點,就是這里獲取的類只能是調用jni的類,不能是其他類。我調用其他的類,出現崩潰問題。然后通過GetMethodID獲取java函數。最后,通過NewGlobalRef新建了回調函數所在的類的實體變量。因為我這里回調的函數不是靜態函數,所以需要新建實體類。


這里需要注意java函數變量對應的簽名,java函數是

public void decodeData(byte[] datas, int dataSize,int sync_code,int frametype,int frameno,int channel,int tv_sec,int tv_msec,int hStream)


對應的獲取函數ID的jni是:

gClassInfo.callbackMethodId = (*env).GetMethodID(cls, "decodeData", "([BIIIIIIII)V");

這個函數簽名比較重要,也比較復雜,我會在寫一遍博客來講解。


具體的講解如下:


在本地方法中調用Java對象的方法的步驟:

1)獲取你需要訪問的Java對象的類

FindClass通過傳java中完整的類名來查找java的class

GetObjectClass通過傳入jni中的一個java的引用來獲取該引用的類型。

他們之間的區別是,前者要求你必須知道完整的類名,后者要求在Jni有一個類的引用。

2)獲取MethodID,調用方法

GetMethodID 得到一個實例的方法的ID

GetStaticMethodID 得到一個靜態方法的ID

3)獲取對象的屬性

GetFieldID 得到一個實例的域的ID

GetStaticFieldID 得到一個靜態的域的ID

JNI通過ID識別域和方法,一個域或方法的ID是任何處理域和方法的函數的必須參數。


3.在線程調用java函數

JNIEnv *env; (*gClassInfo.jvm).AttachCurrentThread(&env, NULL); //調用decodeData方法 env->CallVoidMethod(gClassInfo.obj,gClassInfo.callbackMethodId, jbarray,cFrame->cFrameBuffer.dwBufLen, header->dwDataPacketStartCode,header->bytFrameType, header->dwFrameNo,cFrame->dwChannel,header->dwTimestampBySecond,header->dwTimestampByUSecond/1000,hStream); (*gClassInfo.jvm).DetachCurrentThread();


這里首先獲取到線程的JNIEnv,然后通過CallVoidMethod調用java的decodeData函數。

具體的調用函數的代碼和函數的返回值相關,對應規則如下:

Instance Method Calling Routines:

CallVoidMethodvoid
CallObjectMethodjobject
CallBooleanMethodjboolean
CallByteMethodjbyte
CallCharMethodjchar
CallShortMethodjshort
CallIntMethodjint
CallLongMethodjlong
CallFloatMethodjfloat
CallDoubleMethodjdouble


android JNI層線程回調Java函數就講完了。


就這么簡單。

  與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的android JNI层线程回调Java函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。