JNI 最佳實踐

閱讀本文前,請先閱讀java

  1. JNI-NDK 在AndroidStudio3.2.1版本集成方法(ndk-build方式)
  2. JNI相關概念的理解

★表明難度等級android

★ 實踐一:從C裏返回String給java

以前咱們實現了從C代碼裏返回了一個字符串,代碼以下:數組

  1. java代碼,定義native函數bash

    public class Jni {
        static {
            System.loadLibrary("best");
        }
        public native String sayHello();
    }
    
    複製代碼
  2. javah 生成jni樣式的標準頭文件ide

    這裏略去了生成jni_study_com_jnibsetpractice_Jni.h的具體,下面會再次提到函數

  3. c代碼,實現java裏的native方法工具

    # include "jni_study_com_jnibsetpractice_Jni.h"
    
    JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_sayHello
            (JNIEnv *env, jobject instance) {
        return (*env)->NewStringUTF(env, "Hello from C");
    }
    
    複製代碼
  4. java裏調用native方法post

    bt_1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String hello = jni.sayHello();
                    Toast.makeText(MainActivity.this,hello,Toast.LENGTH_LONG).show();
                }
            });
    複製代碼

★ 實踐二:java傳int參數給C處理,而且返回int

  1. java代碼,定義native函數ui

    public class Jni {
        static {
            System.loadLibrary("best");
        }
    
        public native int add(int x,int y);
    }
    
    複製代碼
  2. javah 生成jni樣式的標準頭文件this

    切換到src/main/java目錄下執行

    javah -d ../jni jni.study.com.jnibsetpractice.Jni
    
    複製代碼

    注意:javah 後半段跟的是 這個native方法所在的類的全路徑名稱

    打開這個文件,發現生成了java裏的這個native方法的jni方法簽名

    JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_add
      (JNIEnv *, jobject, jint, jint);
    複製代碼
    public native int add(int x,int y);
    複製代碼

    對比一下:

    1. 返回值:int對應jint
    2. 方法名:add對應Java_jni_study_com_jnibsetpractice_Jni_add
    3. 參數列表(int ,int )對應(JNIEnv *, jobject, jint, jint)

      這裏說明一下

      參數JNIEnv * :jni的環境,經過它來調用內置的不少jni方法

      參數jobject:表明在java裏調用這個native方法的實例對象(若是是靜態方法表明的是類) 參數jint, jint:對應 int x,int y

      JNIEnv是什麼在JNI相關概念的理解文章裏有詳細解釋

      jint和int關係在JNI相關概念的理解文章裏也有詳細解釋

  3. c代碼,實現java裏的native方法

    # include "jni_study_com_jnibsetpractice_Jni.h"
    JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_add
            (JNIEnv * env, jobject instance, jint x, jint y){
        return x+y;
    }
    複製代碼
  4. Java調用native方法

    bt_2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int add = jni.add(1, 2);
                    Toast.makeText(MainActivity.this, "1+2=" + add, Toast.LENGTH_LONG).show();
                }
            });
    複製代碼

ok,完成了,是否是很簡單,若是java傳遞string給C也是這麼簡單嗎,記得嗎java裏的string與c裏的string但是不同喲

★★ 實踐三:java傳String參數給C處理,而且返回String

  1. java代碼,定義native函數
public class Jni {
    static {
        System.loadLibrary("best");
    }
    public native String transe_string(String str);
}
複製代碼
  1. javah 生成jni樣式的標準頭文件

生成文件的方法略,直接分析這個文件

JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_transe_1string
  (JNIEnv *, jobject, jstring);
複製代碼
public native String transe_string(String str);
複製代碼

對比一下: jstring對應String

看看jstring是什麼

  1. c代碼,實現java裏的native方法

java傳給c一個string,javah生成了方法名後, 發現傳遞來的是一個jstring(由於在c裏,是沒有string的), jstring實際上是void*(任意類型), 咱們須要調用一個方法,把jstring轉爲C語言的char*類型,先看下這個工具方法:

#include <stdlib.h>

/**
 * 把一個jstring轉換成一個c語言的char* 類型.
 */
char* _JString2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env,"GB2312");
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
    jsize alen = (*env)->GetArrayLength(env, barr);
    jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
    if(alen > 0) {
        rtn = (char*)malloc(alen+1); //"\0"
        memcpy(rtn, ba, alen);
        rtn[alen]=0;
    }
    (*env)->ReleaseByteArrayElements(env, barr, ba,0);
    return rtn;
}

複製代碼

實現native方法

JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_transe_1string
        (JNIEnv *env, jobject instance, jstring jstr) {

    //把一個jstring轉換成一個c語言的char* 類型
    char *cStr = _JString2CStr(env, jstr);
    //c語言拼接字符串
    char *cNewStr = strcat(cStr, "簡單加密一下哈哈哈!!!");
    // 把c語言裏的char* 字符串轉成java認識的字符串
    return (*env)->NewStringUTF(env, cNewStr);
}
複製代碼
  1. Java調用native方法
bt_3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String str = jni.transe_string("abc");
                Toast.makeText(MainActivity.this, "abc ====c轉換=====> " + str, Toast.LENGTH_LONG).show();
            }
        });
複製代碼

★★實踐四:java傳遞給C int數組

  1. java代碼,定義native函數
public class Jni {
    static {
        System.loadLibrary("best");
    }

    public native int[] transeIntArray(int[] intArray);
}
複製代碼
  1. javah 生成jni樣式的標準頭文件

生成jni頭文件的方法 略

JNIEXPORT jintArray JNICALL Java_jni_study_com_jnibsetpractice_Jni_transeIntArray
  (JNIEnv *, jobject, jintArray);
複製代碼
public native int[] transeIntArray(int[] intArray);
複製代碼

jintArray對應int[]

  1. c代碼,實現java裏的native方法
JNIEXPORT jintArray JNICALL Java_jni_study_com_jnibsetpractice_Jni_transeIntArray
        (JNIEnv *env, jobject instance, jintArray jArray) {

//    獲得從java傳遞來的int[]的長度
    jsize length = (*env)->GetArrayLength(env, jArray);

//    獲得數組指針
    int *arrayPointer = (*env)->GetIntArrayElements(env, jArray, NULL);
//    開始遍歷數組,把每一個元素加100
    int i;
    for (i = 0; i < length; i++) {
        *(arrayPointer + i) += 100;
    }
// ★★★★將arrayPointer這個int *中值複製到jArray數組中,別忘了這一步驟★★★
    (*env)->SetIntArrayRegion(env,jArray, 0, length, arrayPointer);

//    返回數組
    return jArray;
}
複製代碼
  1. Java調用native方法
bt_4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int[] intArray = new int[]{1, 2, 3};
                int[] intArrayTranse = jni.transeIntArray(intArray);

                for (int i = 0; i < intArray.length; i++) {
                    //發如今這裏遍歷舊的數組,打印出來也是加過100後的數字,101,102,103
                    // 由於C是經過指針操做內存地址,改變了intArray的內存地址的值,
                    // 因此C裏面不返回int[]也是能夠實現目的的
                    Log.e("abc",intArray[i]+"old");
                }

                for (int i = 0; i < intArrayTranse.length; i++) {
                    Log.e("abc",intArrayTranse[i]+"new");
                }//這裏打印的是101,102,103

            }
        });
複製代碼

log以下

實踐五:C調Java

前面講的都是java調用C的方法,有時候咱們需C裏調用Java的方法,

例如:咱們有一個操做圖片的方法是C寫的,在C操做圖片是,須要讓android彈出一個進度條,展現圖片操做的進度,這時候,就須要C代碼裏調用java代碼,來改變UI了

待續。。。。。。。。。。。。。。。。

相關文章
相關標籤/搜索