知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
AndroidNDK開發(fā)(五)——C代碼回調(diào)Java代碼
發(fā)表時間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):61
轉(zhuǎn)載請注明出處:http://blog.csdn.net/allen315410/article/details/41862479
在上篇博客里懂得了Java層是如何傳遞數(shù)據(jù)到C層代碼,并且熟悉了大年夜部分的實際開辟常識,根本上控制這些就可以做一個根本的NDK開辟了,然則光是懂得Java回調(diào)C層的數(shù)據(jù)是不是還不敷啊,推敲問題要推敲可逆性,Java能回調(diào)C,那么C可否反過往返調(diào)Java呢?謎底是肯定可以的,這篇博客就介紹一個C說話若何調(diào)用Java層的代碼。以下是一些問題場景,我們帶著這個問題場景來分析一下實現(xiàn)的過程。
場景1:開辟中C說話層完成了一系列操作后,須要通知Java層代碼此時須要做什么操作。
場景2:大年夜家知道法度榜樣員都是比較懶惰的,Java代碼中封裝了大年夜量的辦法,C法度榜樣員不想反復(fù)寫復(fù)雜的邏輯,這時想經(jīng)由過程C說話回調(diào)應(yīng)用Java層代碼中的辦法。
解釋native辦法callMethod4已經(jīng)運行成功了。
好,帶著膳綾擎的場景,我們下面建立一個小的Demo來測驗測驗解決這些營業(yè)場景的問題。
package com.example.ndkcallback; public class DataProvider { /** * C調(diào)用java空辦法 */ public void nullMethod() { System.out.println("hello from java"); } /** * C調(diào)用java中的帶兩個int參數(shù)的辦法 * * @param x * @param y * @return */ public int Add(int x, int y) { int result = x + y; System.out.println("result in java " + result); return result; } /** * C調(diào)用java中參數(shù)為String的辦法 * * @param s */ public void printString(String s) { System.out.println("java " + s); } // 本處所法 public native void callMethod1(); public native void callMethod2(); public native void callMethod3(); }
編譯頭文件
在DOS敕令行下,切換到工程目次地點的源碼存放的src目次下,應(yīng)用javah敕令編譯C說話的函數(shù)簽名。并且得留意的是,因為我應(yīng)用的JDK 是1.7版本的,所以必須得切換到工程目次/src目次下履行javah,如不雅大年夜家應(yīng)用的是JDK 1.6或者JDK 1.5,那就切換到工程目次/classes目次,履行javah敕令。
[img]http://img.blog.csdn.net/20141211101935855
留意:應(yīng)用javah敕令時,須要指定-encoding utf-8 參數(shù),防止編譯報亂碼缺點,下面是編譯好的頭文件:
有了膳綾擎的頭文件,接下來就是最不好搞的C代碼了,按照套路來,起首把膳綾擎編譯好的頭文件剪切到j(luò)ni目次下,在該目次下新建一個Hello.c的C代碼文件,將剛?cè)侨说念^文件的函數(shù)簽名拷貝到Hello.c中應(yīng)用,然后就是起首惹人LOG日記頭文件,定義LOG日記輸入,再然后就是編譯C代碼,如下:
#include<stdio.h> #include<jni.h> #include"com_example_ndkcallback_DataProvider.h" #include<android/log.h> #define LOG_TAG "System.out.c" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod1 (JNIEnv * env, jobject obj){ //在C說話中調(diào)用Java的空辦法 //1.找到j(luò)ava代碼native辦法地點的字節(jié)碼文件 //jclass (*FindClass)(JNIEnv*, const char*); jclass clazz = (*env)->FindClass(env, "com/example/ndkcallback/DataProvider"); if(clazz == 0){ LOGD("find class error"); return; } LOGD("find class"); //2.找到class瑯綾擎對應(yīng)的辦法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID method1 = (*env)->GetMethodID(env,clazz,"nullMethod","()V"); if(method1 == 0){ LOGD("find method1 error"); return; } LOGD("find method1"); //3.調(diào)用辦法 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, obj, method1); LOGD("method1 called"); } JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod2 (JNIEnv * env, jobject obj) { //1.找到j(luò)ava代碼native辦法地點的字節(jié)碼文件 //jclass (*FindClass)(JNIEnv*, const char*); jclass clazz = (*env)->FindClass(env, "com/example/ndkcallback/DataProvider"); if(clazz == 0){ LOGD("find class error"); return; } LOGD("find class"); //2.找到class瑯綾擎對應(yīng)的辦法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID method2 = (*env)->GetMethodID(env,clazz,"Add","(II)I"); if(method2 == 0){ LOGD("find method2 error"); return; } LOGD("find method2"); //3.調(diào)用辦法 //jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); int result = (*env)->CallIntMethod(env, obj, method2, 3,5); LOGD("result in C = %d", result); } JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod3 (JNIEnv * env, jobject obj) { //1.找到j(luò)ava代碼native辦法地點的字節(jié)碼文件 //jclass (*FindClass)(JNIEnv*, const char*); jclass clazz = (*env)->FindClass(env, "com/example/ndkcallback/DataProvider"); if(clazz == 0){ LOGD("find class error"); return; } LOGD("find class"); //2.找到class瑯綾擎對應(yīng)的辦法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID method3 = (*env)->GetMethodID(env,clazz,"printString","(Ljava/lang/String;)V"); if(method3 == 0){ LOGD("find method3 error"); return; } LOGD("find method3"); //3.調(diào)用辦法 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, obj, method3,(*env)->NewStringUTF(env,"haha in C .")); LOGD("method3 called"); }留意:編寫C代碼時大年夜致須要如下3個重要的步調(diào):
jclass (*FindClass)(JNIEnv*, const char*);
1.找到j(luò)ava代碼native辦法地點的字節(jié)碼文件,在jni.h中的JNINativeInterface中可以找到
個中第1個參數(shù)是JNINativeInterface的指針env,第2個參數(shù)是java辦法地點的類全路徑名,路徑之間用“/”來區(qū)分,弗成以應(yīng)用“.”
2.找到class瑯綾擎對應(yīng)的辦法,在jni.h中的JNINativeInterface中可以找到
獲取風(fēng)靜態(tài)辦法id:
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
獲取靜態(tài)辦法id:
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
個中第1個參數(shù)是JNINativeInterface的指針env,第2個參數(shù)是java字節(jié)碼文件,第3個參數(shù)是java中的辦法名,第四個參數(shù)是java中對應(yīng)辦法的簽名。
3.調(diào)用辦法
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...); jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...); jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...); jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...); jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...); jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__; jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;個中第1個參數(shù)是JNINativeInterface的指針env,第2個參數(shù)是java對象obj,第3個參數(shù)是找到的對應(yīng)java中的辦法,第4個參數(shù)是辦法接收的參數(shù)。這里列出的是常用的辦法,jni.h里的JNINativeInterface供給了大年夜量的辦法情勢用往返調(diào)java中的辦法,想懂得的請參考jni.h這個文件。
應(yīng)用javap敕令查看辦法簽名
JDK為我們供給了如許的一個對象,該對象可以大年夜java字節(jié)碼文件中查看辦法的本地簽名,這個對象就是javap,應(yīng)用前,先在CMD的dos敕令行中,把路徑切換到工程中的java字節(jié)碼文件地點的目次下。
敕令格式:javap -s 包名.辦法地點的Java類名
[img]http://img.blog.csdn.net/20141211151225651
如圖所示的那樣,黃色標注的是辦法名,是(*GetMethodID)(JNIEnv*, jclass, const char*, const char*)中的第3個參數(shù),紅色標注的是辦法簽名,是其第4個參數(shù)。
Android.mk設(shè)備和Application.mk設(shè)備
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := Hello LOCAL_SRC_FILES := Hello.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
APP_PLATFORM := android-8
編譯C代碼
起首在cygwin中切換到當前工程目次下,履行“ndk-build clean”和“ndk-build”敕令
[img]http://img.blog.csdn.net/20141211152804367
在Java中調(diào)用Nattive辦法
public class MainActivity extends Activity implements OnClickListener { static { // 加載動態(tài)庫.so System.loadLibrary("Hello"); } private Button btn1, btn2, btn3; private DataProvider provider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn1 = (Button) findViewById(R.id.btn1); btn2 = (Button) findViewById(R.id.btn2); btn3 = (Button) findViewById(R.id.btn3); btn1.setOnClickListener(this); btn2.setOnClickListener(this); btn3.setOnClickListener(this); provider = new DataProvider(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn1 : // c回調(diào)java中的空辦法 provider.callMethod1(); break; case R.id.btn2 :// c回調(diào)java帶2個int參數(shù)的辦法 provider.callMethod2(); break; case R.id.btn3 :// c回調(diào)java帶string參數(shù)的辦法 provider.callMethod3(); break; default : break; } } }
測試
[img]http://img.blog.csdn.net/20141211153429001
留意:以下測試的LOG中,綠色代表Java生成的LOG,藍色代表C生成的LOG。
測試1:c回調(diào)java中的空辦法
[img]http://img.blog.csdn.net/20141211153444265
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_ndkcallback_DataProvider */ #ifndef _Included_com_example_ndkcallback_DataProvider #define _Included_com_example_ndkcallback_DataProvider #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_ndkcallback_DataProvider * Method: callMethod1 * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod1 (JNIEnv *, jobject); /* * Class: com_example_ndkcallback_DataProvider * Method: callMethod2 * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod2 (JNIEnv *, jobject); /* * Class: com_example_ndkcallback_DataProvider * Method: callMethod3 * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod3 (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
編寫C代碼
測試2:c回調(diào)java帶2個int參數(shù)的辦法
[img]http://img.blog.csdn.net/20141211153448189
測試3:c回調(diào)java帶string參數(shù)的辦法
[img]http://img.blog.csdn.net/20141211153500843
JNIEXPORT void JNICALL Java_com_example_ndkcallback_MainActivity_callMethod4 (JNIEnv * env, jobject obj){ //1.找到j(luò)ava代碼native辦法地點的字節(jié)碼文件 //jclass (*FindClass)(JNIEnv*, const char*); jclass clazz = (*env)->FindClass(env, "com/example/ndkcallback/DataProvider"); if(clazz == 0){ LOGD("find class error"); return; } LOGD("find class"); //2.找到class瑯綾擎對應(yīng)的辦法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID method4 = (*env)->GetMethodID(env,clazz,"nullMethod","()V"); if(method4 == 0){ LOGD("find method4 error"); return; } LOGD("find method4"); //3.經(jīng)由過程jclass獲取jobject //jobject (*AllocObject)(JNIEnv*, jclass); jobject jobj = (*env)->AllocObject(env, clazz); if(jobj == 0){ LOGD("find jobj error"); return; } LOGD("find jobj"); //4.調(diào)用辦法 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, jobj, method4); LOGD("method4 called"); }寫完代碼之后,從新編譯C代碼文件,Refresh和clean一下工程,運行后:
別的:native代碼與調(diào)用的java代碼不在同一個類里
上述建立的Android工程中,native代碼和調(diào)用的java代碼是放在同一個DataProvider類中的,如許在C代碼中調(diào)用Java代碼是異常便利的。然則,平日開辟中我們不必定就這么干,一個項目中java文件很多,如果在其它的java文件中定義了native辦法了,然后再去調(diào)另一個java類里的Java辦法,這種情況下會出現(xiàn)什憒問題呢?帶著這個疑問,我們就在MainActivity.java文件中定義一個native辦法,這個native辦法又要調(diào)用DataProvider類的nullMethod辦法。
在MainActivity.java中,我們定義如許的辦法:
private native void callMethod4();切換到這個src目次下javah獲取函數(shù)簽名,將獲得的簽名頭文件拷貝到j(luò)ni目次下,在C文件中引用這個頭文件,編寫響應(yīng)的C代碼:
JNIEXPORT void JNICALL Java_com_example_ndkcallback_MainActivity_callMethod4 (JNIEnv * env, jobject obj){ //1.找到j(luò)ava代碼native辦法地點的字節(jié)碼文件 //jclass (*FindClass)(JNIEnv*, const char*); jclass clazz = (*env)->FindClass(env, "com/example/ndkcallback/DataProvider"); if(clazz == 0){ LOGD("find class error"); return; } LOGD("find class"); //2.找到class瑯綾擎對應(yīng)的辦法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID method4 = (*env)->GetMethodID(env,clazz,"nullMethod","()V"); if(method4 == 0){ LOGD("find method4 error"); return; } LOGD("find method4"); //3.調(diào)用辦法 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, obj, method4); LOGD("method4 called"); }編譯運行之后,報錯了
[img]http://img.blog.csdn.net/20141211171250055
實際運行的時刻,法度榜樣直接崩潰了,查看日記發(fā)明,字節(jié)碼class找到了,辦法method找到了,然則就是沒有履行method辦法,顯然是履行method辦法這行代碼出了Bug,以下是調(diào)用method辦法履行的代碼:
(*env)->CallVoidMethod(env, obj, method4);那么這行代碼是為什么報錯了呢?細心不雅察一下,CallVoidMethod辦法的第2個參數(shù)obj,這個obj是jobject類型的,默認是java native辦法地點的類的對象,就是MainActivity類的對象,然則這個native辦法實際上調(diào)用的java辦法存在于DataProvider類的nullMethod,調(diào)用nullMethod顯然須要應(yīng)用DataProvider類的對象。反正就一句話:obj對象不精確,須要java辦法對應(yīng)的對象,即DataProvider。
創(chuàng)建工程,在工程瑯綾擎定義Java辦法和Native辦法
知道問題了,就可以著手解決問題了。在jni.h的頭文件中,JNINativeInterface供給了如許的一個辦法,贊助我們經(jīng)由過程字節(jié)碼jclass找到對應(yīng)的對象:
jobject (*AllocObject)(JNIEnv*, jclass);這個辦法第1個參數(shù)是JNINativeInterface,第2個參數(shù)是jclass,返回值jobject。我們就拿這個辦法獲取jobject,傳給CallVoidMethod:
[img]http://img.blog.csdn.net/20141211173234721
源碼請在這里下載
相關(guān)案例查看更多
相關(guān)閱讀
- 模版信息
- 網(wǎng)站建設(shè)首頁
- 云南網(wǎng)絡(luò)營銷
- 云南網(wǎng)站建設(shè)公司哪家好
- 云南做百度小程序的公司
- 云南網(wǎng)站建設(shè)價格
- 云南網(wǎng)站建設(shè)一條龍
- 定制小程序開發(fā)
- 區(qū)塊鏈
- 云南軟件定制公司
- 北京小程序制作
- 報廢車管理
- 小程序商城
- 報廢車回收管理軟件
- 昆明軟件定制公司
- 小程序被攻擊
- 小程序表單
- 網(wǎng)站制作
- 昆明做網(wǎng)站建設(shè)的公司排名
- 云南軟件開發(fā)
- 云南小程序開發(fā)
- 云南軟件定制
- 云南網(wǎng)站建設(shè)百度
- 云南網(wǎng)站制作
- 云南建設(shè)廳網(wǎng)站首頁
- 人口普查小程序
- 南通小程序制作公司
- 云南科技公司
- 網(wǎng)站建設(shè)靠譜公司
- 云南網(wǎng)站建設(shè)哪家強