JNI C反射調用java方法

前面記錄了調用C的學習筆記,如今來記錄一下C反射調用Java的筆記。
JNI開發學習之調用C方法 
Android開發中調用一個類中沒有公開的方法,能夠進行反射調用,而JNI開發中C調用java的方法也是反射調用。java

C代碼回調Java方法步驟:
①獲取字節碼對象(jclass (FindClass)(JNIEnv, const char*);)git

②經過字節碼對象找到方法對象(jmethodID (GetMethodID)(JNIEnv, jclass, const char, const char);)github

③經過字節碼文件建立一個object對象(該方法可選,方法中已經傳遞一個object,若是須要調用的方法與本地方法不在同一個文件夾則須要新建立object(jobject ( AllocObject)(JNIEnv, jclass);),若是須要反射調用的java方法與本地方法不在同一個類中,須要建立該方法,可是若是是這樣,而且須要跟新UI操做,例如打印一個Toast 會報空指針異常,由於這時候調用的方法只是一個方法,沒有actiivty的生命週期。(下面有解決方案))

④經過對象調用方法,能夠調用空參數方法,也能夠調用有參數方法,而且將參數經過調用的方法傳入(void (CallVoidMethod)(JNIEnv, jobject, jmethodID, ...);)架構

首先,也是按照前面的步驟新建一個 import C++ 工程,新建ccalljava.c 和一個JNI.java文件(別忘了修改CMakeLists.txt對應C方法的名字和路徑)函數

JNI.java中編寫本地方法:佈局

//C調用java空方法
public native void callbackmethod();
//C調用java中的帶兩個int參數的方法
public native void callbackIntmethod();
//C調用java中參數爲string的方法
public native void callbackStringmethod();
//C調用java中靜態方法
public native void callStaticmethod();

而且編寫被C反調的java方法:學習

//C調用java空方法
public void helloFromJava(){    
Toast.makeText(context, "C調用了java的空方法",Toast.LENGTH_SHORT ).show();}
//C調用java中的帶兩個int參數的方法
public int add(int x,int y) {   
return x+y;}
//C調用java中參數爲string的方法
public void printString(String s){    
Toast.makeText(context, s, Toast.LENGTH_SHORT).show();}
//C調用java中靜態方法
public static void staticmethod(String s){   
Log.w("毛麒添",s+",我是被C調用的靜態方法");}

下面來編寫ccalljava.c中的C方法gradle

/**C函數反射調用java中的空方法 */
JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callbackmethod(JNIEnv *env, jobject object) {    
 jclass jclazz = (*env)->FindClass(env, "com/mao/ccalljava/JNI");   
jmethodID methodID = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V");      
(*env)->CallVoidMethod(env,object,methodID);}
/**
 調用java中Int方法   
 */
JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callbackIntmethod(JNIEnv *env, jobject object) {    
 jclass clzz=(*env)->FindClass(env,"com/mao/ccalljava/JNI");   
 jmethodID methodID=(*env)->GetMethodID(env,clzz,"add","(II)I");    
int result=(*env)->CallIntMethod(env,object,methodID,3,4);   
 //logcat 打印相加返回的結果   
 LOGD("RESLUT = %d",result);
}
/**
調用java中String方法     
*/
JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callbackStringmethod(JNIEnv *env, jobject object) {
//先獲取字節碼對象  jclass      (*FindClass)(JNIEnv*, const char*);   
 jclass clzz=(*env)->FindClass(env,"com/mao/ccalljava/JNI");   
 //獲取method對象   jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);   
 jmethodID methodID=(*env)->GetMethodID(env,clzz,"printString","(Ljava/lang/String;)V");   
 //將要傳遞的字符串先轉換成jstring類型 ,而後在傳遞給java方法    int result=(*env)->NewStringUTF(env,"hello form C/C++ ");    (*env)->CallVoidMethod(env,object,methodID,result);
}
/**
調用Java中的靜態方
 */
JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callStaticmethod(JNIEnv *env, jobject instance) {   
 jclass clzz=(*env)->FindClass(env,"com/mao/ccalljava/JNI");    
jmethodID methodID=(*env)->GetStaticMethodID(env,clzz,"staticmethod","(Ljava/lang/String;)V");   
 jstring str = (*env)->NewStringUTF(env, "C調用java");   
 (*env)->CallStaticVoidMethod(env,clzz,methodID,str);
}

經過字節碼對象找到方法對象,該方法中的第四個參數是方法簽名ui

jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);

獲取方法簽名的方法是進入工程目的 ..../build/classes/debug 進入控制檯,
輸入命令 javap -s 要獲取方法的路徑(例如本例 javap -s com.mao.ccalljava.JNI)this

上面步驟二中提到的沒有生命週期的解決方法:
報空指針,主要就是沒上下文環境,反射調用的方法是new出來的,也會沒有生命週期.這時候就能夠將本地方法和調用的方法都放在同一個類中,沒有上下文環境就在建立方法的時候在構造方法中接收一個。

private Context context;
public JNI(Context context){    
  this.context=context;
}

最後,別忘了添加在JNI.java中添加動態連接庫文件(佈局和MianActiivty中邏輯比較簡單,這裏

static {    
    System.loadLibrary("ccalljava");
}

在gradle 配置一些處理器架構

externalNativeBuild {   
 cmake {       
 cppFlags ""       
 // Clang是一個C語言、Objective-C、C++語言的輕量級編譯器。        
arguments "-DANDROID_TOOLCHAIN=clang"        
// 生成.so庫的目標平臺        
abiFilters "armeabi-v7a" , "armeabi" ,"x86"    
        }
}

接下來在工程編譯經過後能夠該目錄下找到不一樣處理器架構的動態連接庫文件

最後,上幾張運行成功的截圖:

例子源碼地址:https://github.com/maoqitian/CcallJava

相關文章
相關標籤/搜索