JNI通过线程c回调java层的函数
1、參看博客:http://www.jianshu.com/p/e576c7e1c403
Android JNI 篇 - JNI回調(diào)的三種方法(精華篇)
2、參看博客:?
JNI層線程回調(diào)Java函數(shù)關(guān)鍵點(diǎn)及示例
http://blog.csdn.net/fu_shuwu/article/details/41121741
?3?http://blog.csdn.net/u010402982/article/details/48199487
核心的關(guān)鍵點(diǎn):
三、本地線程中調(diào)用java對(duì)象
問(wèn)題1:
JNIEnv是一個(gè)線程相關(guān)的變量
JNIEnv 對(duì)于每個(gè) thread 而言是唯一的
JNIEnv *env指針不可以為多個(gè)線程共用
解決辦法:
但是java虛擬機(jī)的JavaVM指針是整個(gè)jvm公用的,我們可以通過(guò)JavaVM來(lái)得到當(dāng)前線程的JNIEnv指針.
可以使用javaAttachThread保證取得當(dāng)前線程的Jni環(huán)境變量
static JavaVM *gs_jvm=NULL;
gs_jvm->AttachCurrentThread((void **)&env, NULL);//附加當(dāng)前線程到一個(gè)Java虛擬機(jī)
jclass cls = env->GetObjectClass(gs_object);
jfieldID fieldPtr = env->GetFieldID(cls,"value","I");
問(wèn)題2:
不能直接保存一個(gè)線程中的jobject指針到全局變量中,然后在另外一個(gè)線程中使用它。
解決辦法:
用env->NewGlobalRef創(chuàng)建一個(gè)全局變量,將傳入的obj(局部變量)保存到全局變量中,其他線程可以使用這個(gè)全局變量來(lái)操縱這個(gè)java對(duì)象
注意:若不是一個(gè) jobject,則不需要這么做。如:
jclass 是由 jobject public 繼承而來(lái)的子類(lèi),所以它當(dāng)然是一個(gè) jobject,需要?jiǎng)?chuàng)建一個(gè) global reference 以便日后使用。
而 jmethodID/jfieldID 與 jobject 沒(méi)有繼承關(guān)系,它不是一個(gè) jobject,只是個(gè)整數(shù),所以不存在被釋放與否的問(wèn)題,可保存后直接使用。
?
總結(jié):創(chuàng)建兩個(gè)全局的變量一個(gè)是JavaVM 虛擬機(jī)環(huán)境jvm
另外一個(gè)全局變量是jobj對(duì)象
然后創(chuàng)建一個(gè)線程,使用全局的jvm獲得與該線程一一對(duì)應(yīng)的env,通過(guò)env和全局的jobj對(duì)象,創(chuàng)建java層的對(duì)象,調(diào)用java層的方法,最近將線程環(huán)境關(guān)閉。
?
我們來(lái)看下程序的框架:
我們來(lái)看下程序的代碼:
package im.weiyuan.com.jni;public class Sdk {static {System.loadLibrary("hello");}public Sdk() {}//單例private static class SdkHodler {static Sdk instance = new Sdk();}public static Sdk getInstance() {return SdkHodler.instance;}//回調(diào)到各個(gè)線程public interface OnSubProgressListener {public int onProgressChange(long total, long already);}; //c層回調(diào)上來(lái)的方法public int onProgressCallBack(long total, long already) {//自行執(zhí)行回調(diào)后的操作System.out.println("total:"+total);System.out.println("already:"+already);return 1;}//調(diào)到C層的方法public native void nativeDownload();}然后
(一)?? 第二步:make project一下,目的就是編譯成對(duì)應(yīng)的class文件。然后根據(jù)生成的class文件,利用javah生成對(duì)應(yīng)的 .h頭文件。
?
(一)?? 第三步:
Cmd終端進(jìn)入到你新建的android工程的src/main目錄下:我的目錄是:
F:\JNI\app\src\main
執(zhí)行命令:
Javah -d jni -classpath D:\android_sdk_ndk\sdk\platforms\android-21\android.jar;..\..\build\intermediates\classes\debug im.weiyuan.com.jni.Sdk
其中:?D:\android_sdk_ndk\sdk\是你sdk的路徑?
?im.weiyuan.com.jni.Sdk是你對(duì)應(yīng)的
就是你聲明的native函數(shù)所在的包名加上類(lèi)名。
就會(huì)發(fā)現(xiàn)在main目錄下多了一個(gè)jni文件夾,里面有生成好的頭文件:
?
在這個(gè)頭文件中就自動(dòng)幫助我們生成了函數(shù)的聲明
第五步:
在jni目錄下新建一個(gè) .c文件。來(lái)實(shí)現(xiàn)頭文件里面聲明的方法。我的叫im_weiyuan_com_jni_Sdk.c
我們來(lái)看下程序的代碼:
// // Created by wei.yuan on 2017/6/13. // #include <jni.h> #include <string.h> #include <pthread.h> #include "im_weiyuan_com_jni_Sdk.h" #include "im_weiyuan_com_jni_Sdk_OnSubProgressListener.h" #include "im_weiyuan_com_jni_Sdk_SdkHodler.h" JavaVM *g_VM; jobject g_obj; #include <jni.h> #include <string.h> #include <android/log.h> #include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dlfcn.h> #include <assert.h> #include <android\log.h> #include <errno.h> #include <pthread.h>static void* native_thread_exec(void* arg){JNIEnv *env;int mNeedDetach = -1;//獲取當(dāng)前native線程是否有沒(méi)有被附加到j(luò)vm環(huán)境中int getEnvStat = (*g_VM)->GetEnv(g_VM, (void **) &env,JNI_VERSION_1_6);if (getEnvStat == JNI_EDETACHED) {//如果沒(méi)有, 主動(dòng)附加到j(luò)vm環(huán)境中,獲取到envif ((*g_VM)->AttachCurrentThread(g_VM, &env, NULL) != 0) {return;}mNeedDetach = JNI_TRUE;}//通過(guò)全局變量g_obj 獲取到要回調(diào)的類(lèi)jclass javaClass = (*env)->GetObjectClass(env, g_obj);if (javaClass == 0) {// LOGI("Unable to find class");(*g_VM)->DetachCurrentThread(g_VM);return;}//獲取要回調(diào)的方法IDjmethodID javaCallbackId = (*env)->GetMethodID(env, javaClass,"onProgressCallBack", "(JJ)I");if (javaCallbackId == NULL) {//LOGI("Unable to find method:onProgressCallBack");return;}//執(zhí)行回調(diào)(*env)->CallIntMethod(env, g_obj, javaCallbackId,100,100);//釋放當(dāng)前線程if(mNeedDetach) {(*g_VM)->DetachCurrentThread(g_VM);}//釋放你的全局引用的接口,生命周期自己把控
(*env)->DeleteGlobalRef(env, g_obj);
g_obj = NULL; env = NULL;
} JNIEXPORT void JNICALL Java_im_weiyuan_com_jni_Sdk_nativeDownload (JNIEnv * env , jobject thiz){//JavaVM是虛擬機(jī)在JNI中的表示,等下再其他線程回調(diào)java層需要用到(*env)->GetJavaVM(env, &g_VM);// 生成一個(gè)全局引用保留下來(lái),以便回調(diào)g_obj = (*env)->NewGlobalRef(env, thiz);// 此處使用c語(yǔ)言開(kāi)啟一個(gè)線程,進(jìn)行回調(diào),這時(shí)候java層就不會(huì)阻塞,只是在等待回調(diào) pthread_t thread_id;if(( pthread_create(&thread_id,NULL, native_thread_exec,NULL))!=0){return ;}return; }
其中:
JNIEXPORT void JNICALL Java_im_weiyuan_com_jni_Sdk_nativeDownload (JNIEnv * env , jobject thiz)就是在im_weiyuan_com_jni_Sdk.h頭文件中系統(tǒng)自動(dòng)生成的(一)?? 第五步:配置ndk的路徑
在 local.properties 文件中設(shè)置ndk的路徑:
?
sdk.dir=D\:\\android_sdk_ndk\\sdkndk.dir=D\:\\android_sdk_ndk\\android-ndk-r10e
?
(一)?? 在gradle.propertes中添加
android.useDeprecatedNdk=true
http://stackoverflow.com/questions/31979965/after-updating-android-studio-to-version-1-3-0-i-am-getting-ndk-integration-is
?
?
在app目錄下的 build.gradle中設(shè)置庫(kù)文件名(生成的so文件名):
?
找到 defaultConfig 這項(xiàng),在里面添加如下內(nèi)容:
??????? ndk{
?????????? ?moduleName "hello"? //設(shè)置庫(kù)(so)文件名稱(chēng)
??????????? abiFilters "armeabi", "armeabi-v7a", "x86"?
??????? }
這里??? hello必須和?
?static {
??????? System.loadLibrary("hello");
}中的名字是對(duì)應(yīng)的,abiFilters是指定生成那種平臺(tái)下的so庫(kù),對(duì)應(yīng)于eclipse中的Aplication.mk文件中的內(nèi)容。編譯,并運(yùn)行。界面上就會(huì)顯示從native方法傳過(guò)來(lái)的值。
在c代碼中 "onProgressCallBack", "(JJ)I"這里調(diào)用上層java函數(shù)的時(shí)候,使用到了函數(shù)的簽名:
如何得到函數(shù)的簽名了:
如何查看函數(shù)的簽名:
如果當(dāng)前的工程存放的目錄在F盤(pán)下的JNI目錄:
進(jìn)入到工程的F:\JNI\app\build\intermediates\classes\debug 目錄下
執(zhí)行命令:
Javap? -s? im.weiyuan.com.jni.Sdk
?
其中im.weiyuan.com.jni是包名,Sdk是類(lèi)名
F:\JNI\app\build\intermediates\classes\debug> javap -s im.weiyuan.com.jni.Sdk
Compiled from "Sdk.java"
public class im.weiyuan.com.jni.Sdk {
? public im.weiyuan.com.jni.Sdk();
??? descriptor: ()V
?
? public static im.weiyuan.com.jni.Sdk getInstance();
??? descriptor: ()Lim/weiyuan/com/jni/Sdk;
?
? public int onProgressCallBack(long, long);
??? descriptor: (JJ)I
?
? public native void nativeDownload();
??? descriptor: ()V
?
? static {};
??? descriptor: ()V
}
?
在activity中我們可以調(diào)用native函數(shù)的代碼:
package im.weiyuan.com.jni;import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//調(diào)用native的方法 Sdk.getInstance().nativeDownload();} }我們來(lái)看下程序的運(yùn)行結(jié)果:
06-13 15:38:40.070 14935-15032/? I/System.out: total:100
android studio 的代碼地址:
?http://download.csdn.net/detail/jksfkdjksdfjkjk/9869331
生成的so的目錄如下所示:
?
轉(zhuǎn)載于:https://www.cnblogs.com/kebibuluan/p/7001705.html
總結(jié)
以上是生活随笔為你收集整理的JNI通过线程c回调java层的函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梦到老公和我离婚了是什么征兆
- 下一篇: 孕妇梦到挖凉薯是什么意思