JNI/NDK Java調用C/C++前言
經過第三篇文章講解在實際的開發過程當中Java層調用C/C++層的處理流程。其實咱們在很大的業務裏也須要C/C+ +層去調用Java層,這兩層之間的相互調用顯得如此的重要,正式兩層之間的相互調用使得程序更具備高效性、安全性可言。下面主要講解一下C/C+ +層調用Java層的處理流程。java
JNI/NDK Java調用C/C++ 編寫java文件
一樣咱們也須要先寫java文件,用來讓C/C++調用java層的方法,實現具體的業務邏輯。
android
public class NativeUtils { //一、引用JIN/NDk庫文件(庫名稱與建立的C/C++文件名保持一致) static { System.loadLibrary("jni-utils"); } //二、定義native 原生方法 (表明該方法會調用C/C++來實現功能) //有返回值、無參數 處理字符串 public native String JavaCallJNI(); //有參數、有返回值 處理int類型 public native int JavaCallJNISum(int num1, int num2); //有參數、有返回值 處理int[] 數組類型 public native int[] JavaCallJNIArr(int[] arr); //C/C++層調用該方法的回調 public native void JNICallJavaBack(); //java層方法的具體實現 public void JNICallJava(String msg) { Log.e("TAG", "JNICallJava--->" + msg); } //C/C++層調用該方法的回調 public native void JNICallJavaSumBack(); //java層方法的具體實現 public void JNICallJavaSum(int num1, int num2) { Log.e("TAG", String.format("JNICallJavaSum--->%d+%d=%d", num1, num2, num1 + num2)); } //C/C++層調用該方法的回調 public native void JNICallJavaStaticMethodBack(); //java層方法的具體實現 public static void JNICallJavaStaticMethod() { Log.e("TAG", "JNICallJavaStaticMethod--->"); } }
經過上一步咱們編寫了java層的代碼,其次咱們須要在C/C++文件中進行調用java層的方法。這是一個比較抽象的處理。整體思想就是採用反射機制拿到方法的信息。數組
//1.引入Jni頭文件 #include <jni.h> #include <stdlib.h> #include <stdio.h> #include <android/log.h> //處理日誌打印 //------------------------------------日誌處理----------------------------------- #define LOG_TAG "JNILogTag" //不帶格式log #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,"%s",__VA_ARGS__) //帶格式 #define LOG_I(format, ...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,format,__VA_ARGS__) //2.編寫NativeUtils對應的JNI的C/C++函數 //------------------------------C/C++函數解釋-------------------------------------- //JNIEXPORT JNI導出 jstring 函數返回值 JNICALL JNI進行調用 //Java_全類名_NativeUtils方法名(JNIEnv *env,jobject jobject) // JNIEnv *env C/C++中的函數指針 jobject jobject 調用Native方法的類對象 //------------------------------------sig簽名處理----------------------------------- //方式一:命令 //生成方法簽名的方式:進行生成.class文件的目錄下 執行: javap -s xxx.class //方式二:規律 public String JNICallJava(String msg) java層的方法 //首先是參數(String msg) ---> (Ljava/lang/String;) //其次返回值 String ----> Ljava/lang/String; //最終簽名 (Ljava/lang/String;)Ljava/lang/String; //------------------------常見的轉換表------------------------ // String Ljava/lang/String; // int I // int[] [I // void V //------------------------------------C/C++調用Java----------------------------------- extern "C" JNIEXPORT void JNICALL Java_com_aynu_androidjni_NativeUtils_JNICallJavaBack(JNIEnv *env, jobject instance) { //1.獲得類的字節碼 (調用java方法所在的類 包名+類名) jclass cls = env->FindClass("com/aynu/androidjni/NativeUtils"); //2.獲取方法id //clazz 類的字節碼 name java方法名稱 sig java方法簽名 jmethodID mid = env->GetMethodID(cls, "JNICallJava", "(Ljava/lang/String;)V"); //3.實例化該類 jobject jobject = env->AllocObject(cls); //5.設置java層參數的值 jstring str = env->NewStringUTF("C/C++ input value"); //4.調用java層方法 env->CallVoidMethod(jobject, mid, str); } extern "C" JNIEXPORT void JNICALL Java_com_aynu_androidjni_NativeUtils_JNICallJavaSumBack(JNIEnv *env, jobject instance) { //1.獲得類的字節碼 (調用java方法所在的類 包名+類名) jclass cls = env->FindClass("com/aynu/androidjni/NativeUtils"); //2.獲取方法id //參數解析 jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) //clazz 類的字節碼 name java方法名稱 sig java方法簽名 jmethodID mid = env->GetMethodID(cls, "JNICallJavaSum", "(II)V"); //3.實例化該類 jobject jobject = env->AllocObject(cls); //5.設置java層參數的值 jint num1 = 10; jint num2 = 5; //4.調用java層方法 env->CallVoidMethod(jobject, mid, num1, num2); } extern "C" JNIEXPORT void JNICALL Java_com_aynu_androidjni_NativeUtils_JNICallJavaStaticMethodBack(JNIEnv *env, jobject instance) { //1.獲得類的字節碼 (調用java方法所在的類 包名+類名) jclass cls = env->FindClass("com/aynu/androidjni/NativeUtils"); //2.獲取方法id //參數解析 jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) //clazz 類的字節碼 name java方法名稱 sig java方法簽名 jmethodID mid = env->GetStaticMethodID(cls, "JNICallJavaStaticMethod", "()V"); //3.實例化該類 該方法爲static靜態方法 故不須要實例化 //jobject jobject = env->AllocObject(cls); //4.調用java層方法 env->CallStaticVoidMethod(cls, mid); } //------------------------------------Java調用C/C++----------------------------------- extern "C" JNIEXPORT jintArray JNICALL Java_com_aynu_androidjni_NativeUtils_JavaCallJNIArr(JNIEnv *env, jobject jobject, jintArray arr_) { //1.獲取arr數組的元素 jint *arr = env->GetIntArrayElements(arr_, NULL); //2.獲取arr數組的長度 jsize arrSize = env->GetArrayLength(arr_); //3.遍歷數組 for (int i = 0; i < arrSize; ++i) { *(arr + i) += 10; } //4.釋放內存 env->ReleaseIntArrayElements(arr_, arr, 0); //5.返回數組 return arr_; } extern "C" JNIEXPORT jint JNICALL Java_com_aynu_androidjni_NativeUtils_JavaCallJNISum(JNIEnv *env, jobject jobject, jint num1, jint num2) { //1.相應的邏輯運算 return num1 + num2; } extern "C" JNIEXPORT jstring JNICALL Java_com_aynu_androidjni_NativeUtils_JavaCallJNI(JNIEnv *env, jobject jobject) { //3.編寫具體的業務邏輯 return env->NewStringUTF("C/C++ Say"); }
咱們編寫好java層和C/C++層以後,就須要咱們進行去調用。 安全
public class MainActivity extends AppCompatActivity { private TextView mMsgTxt; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mMsgTxt = (TextView) findViewById(R.id.msg_txt); NativeUtils nativeUtils = new NativeUtils(); //Java調用JNI String msg = nativeUtils.JavaCallJNI(); //mMsgTxt.setText(msg); //Java調用JNI實現兩個數之和 int sum = nativeUtils.JavaCallJNISum(10, 5); //mMsgTxt.setText(String.format("10+5=%d", sum)); //Java調用JNI實現數組中的每一個元素加10 int[] arr = new int[]{1, 2, 3, 4, 5}; int[] jniArr = nativeUtils.JavaCallJNIArr(arr); StringBuilder buffer = new StringBuilder(); for (int aJniArr : jniArr) { buffer.append(aJniArr).append(","); } //mMsgTxt.setText(buffer.toString()); //JNICallJavaBack執行 nativeUtils.JNICallJavaBack(); //JNICallJavaSumBack執行 nativeUtils.JNICallJavaSumBack(); //JNICallJavaStaticMethodBack執行 nativeUtils.JNICallJavaStaticMethodBack(); } }
調用以後咱們會生成對應的so庫文件,同時也會展現咱們的最後結果。app
以上即是採用Androidstudio+CMake進行搭建JNI/NDK開發中C/ C++調用Java代碼流程的項目。如如有理解錯誤的地方,請多多留言指教。ide