JNI NDK (AndroidStudio+CMake )實現C C++調用Java代碼流程

 

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--->");
    }
}

JNI/NDK Java調用C/C++ 編寫C/C++文件

經過上一步咱們編寫了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");
}

JNI/NDK 進行調用

咱們編寫好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();
    }
}

JNI/NDK 結果

調用以後咱們會生成對應的so庫文件,同時也會展現咱們的最後結果。app

JNI/NDK 結束語

以上即是採用Androidstudio+CMake進行搭建JNI/NDK開發中C/ C++調用Java代碼流程的項目。如如有理解錯誤的地方,請多多留言指教。ide

相關文章
相關標籤/搜索