Adnroid jni數據傳遞大全,看這篇就夠了

前言

        這次總結一下jni數據交互(通訊)的方式,本篇側重應用,native層主要用C++編寫。掌握數據交互方式,對入門jni及理解java與C++的數據類型映射起到四兩撥千斤的作用。本編着重jni數據傳遞實現,跳過jni開發環境搭建及jni基礎知識(網上基本可以找到對應解決方案)。

        平時爲了實現某個功能(RFID編解碼庫),如果C++開發那邊已經有現成的C++類庫,再用java進行重複的工作就沒必要了。我們只需提供接口java層調用即可,如果C++大佬沒空,接口層代碼自己也可以實現,最後在編譯成Android平臺調用的so庫。so庫可以自己使用,也可以給客戶使用,不用擔心代碼被反編譯的問題。

本次實現jni數據交互方式(jni靜態註冊)如圖所示: 

 

jni編譯的方式

  1. 使用Cmake文件編譯
  2. 使用mk文件編譯(這次使用mk文件編譯)

環境(知識jni開發的環境)準備:

這兩種方式都可以編譯調試 ,如果只是調試native與java層代碼,是不需要執行ndk-build命令(生成不同cpu架構的so庫)。

jni開發流程

  1. 編寫java本地方法
  2. 生成.h頭文件 , 使用java命令是javah(必須在包名外使用javah命令)

  1. 創建jni目錄,引入頭文件,根據頭文件實現C/C++代碼
  2. 編寫Android.mk、Application.mk文件
  3. Ndk編譯生成動態庫
  4. Java代碼load動態庫,調用native代碼

知識準備

java方法簽名查看

查看系統java系統方法 

javap -s java.lang.String 

查看自定義類(在class目錄下執行):

javap -s  com.sjl.jnidata.JniDataTransfer

C和C++函數時的JNI使用區別 

jni.h頭文件中對於*.c & *.cpp採用不同的定義,在C的定義中,env是二級指針,C需要對env指針進行雙重deferencing,而且須將env作爲第一個參數傳給jni函數;而在C++的定義中,env是一級指針。

  • 在 C 中,JNI 函數調用由“(*env)->”作前綴,目的是爲了取出函數指針所引用的值;
  • 在 C++ 中,JNIEnv 類擁有處理函數指針查找的內聯成員函數。

舉栗子:
C 語法(.c文件):jsize len = (*env)->GetArrayLength(env,array);
C++ 語法( .cpp文件):jsize len =env->GetArrayLength(array);

常見錯誤 

1. javah生成頭文件報錯:編碼GBK的不可映射字符

解決方法:javah -jni -encoding UTF-8  類名

2.運行項目報錯:

解決方法:

 退出應用或者點擊終止項目或者清理項目

 jni數據傳遞

        native層處理java層數據,簡單說就是C++仿java的寫法,java幾行代碼實現,在native實現可能需要多行實現。舉個例子簡單說明一下,分別用java構造器java反射,C++三種方式創建同一個對象作爲對比。

假設有這麼一個類Simple:

package com.sjl.jnidata;

public class Simple {

    private String a;
    private int b;

    public Simple() {
    }

    public Simple(String a, int b) {
        this.a = a;
        this.b = b;
    }
  //省略get,set方法...
}

 使用java構造器:

 Simple simple = new Simple("李四",99);

 使用java反射:

  try {
            Class<?> clz = Class.forName("com.sjl.jnidata.Simple");
            Constructor<?> constructor = clz.getConstructor(String.class, int.class);
            Object obj = constructor.newInstance("李四", 99);
            Simple simple = (Simple) obj;
        } catch (Exception e) {
            e.printStackTrace();
        }

 使用C++:

jclass simpleClz = env->FindClass("com/sjl/jnidata/Simple");
jmethodID constructorMID = env->GetMethodID(simpleClz, "<init>", "(Ljava/lang/String;I)V");//獲取Simple的構造器
jstring str = env->NewStringUTF("李四");
jobject object = env->NewObject(simpleClz, constructorMID, str, 99);//object就是Simple對象 

從上面發現C++創建對象與java的反射創建對象類似 。

回到正題,介紹jni數據傳遞,下面所有native方法所在類名爲:JniDataTransfer

 1.java傳遞基本類型數據到C++(帶參數且有返回值)

java native:

/**
     * 測試基本類型
     *
     * @param b
     * @param i
     * @param c
     * @param d
     * @param str
     * @return
     */
    public static native String testPrimitiveType(byte b, int i, char c, double d, String str);

C++實現:

/*
 * 帶參數且有返回值
 */
JNIEXPORT jstring JNICALL Java_com_sjl_jnidata_JniDataTransfer_testPrimitiveType
        (JNIEnv *env, jclass clz, jbyte b, jint i, jchar c, jdouble d, jstring str) {
    int index = (int) (b + i + c + d);
    LOGI("index=%d", index);
    const char *strContent = env->GetStringUTFChars(str, JNI_FALSE);
    env->ReleaseStringUTFChars(str, strContent);
    return env->NewStringUTF(strContent);
}

測試示例:

        String hello = JniDataTransfer.testPrimitiveType((byte) 0x1, 2, '3', 4.5, "hello world");

此處返回hello world. 

 2.java傳遞基本類型數據到C++(無參數且有返回值) 

java native:

/**
     * 獲取當前時間戳
     *
     * @return
     */
    public static native long getCurrentTime();

 C++實現:


/**
 * 獲取當前時間
 * @return
 */
long long getCurrentTime() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    long long ts = (long long) tv.tv_sec * 1000 + tv.tv_usec / 1000;//64位,不加在64位系統會溢出,導致時間不準
    return ts;
}


/*
 * 無參數且有返回值
 */
JNIEXPORT jlong JNICALL Java_com_sjl_jnidata_JniDataTransfer_getCurrentTime
        (JNIEnv *env, jclass clz) {
    long long time = getCurrentTime();
    return time;
}

 測試示例:

long currentTime = JniDataTransfer.getCurrentTime();

 輸出結果:

3.java傳遞基本類型數據到C++(用byte[]接收數據,並返回數據長度) 

java native:

   /**
     * 這個實際用的比較多
     *
     * @param sendData
     * @param resultData
     * @return
     */
    public static native int testBytes(byte[] sendData, byte[] resultData);

  C++實現:

/*
 *用byte[]接收數據
 */
JNIEXPORT jint JNICALL Java_com_sjl_jnidata_JniDataTransfer_testBytes
        (JNIEnv *env, jclass clz, jbyteArray sendData, jbyteArray resultData) {
    jbyte *send = env->GetByteArrayElements(sendData, NULL);
    int nLength = env->GetArrayLength(sendData);//字節數組長度
    jbyte *result = env->GetByteArrayElements(resultData, NULL);
    int nLength2 = env->GetArrayLength(resultData);//字節數組長度
    if (nLength > nLength2) {
        env->ReleaseByteArrayElements(sendData, send, 0);//釋放
        env->ReleaseByteArrayElements(resultData, result, 0);//釋放
        return -1;
    }
    for (int i = 0; i < nLength; i++) {
        result[i] = send[i];
    }
    env->ReleaseByteArrayElements(sendData, send, 0);//釋放
    env->ReleaseByteArrayElements(resultData, result, 0);//釋放
    return nLength;
}

測試示例:

 byte[] sendData = new String("hello world").getBytes();
 byte[] resultData = new byte[32];
 int length = JniDataTransfer.testBytes(sendData, resultData);

resultData轉成字符串就是hello world

 4.java傳據遞引用數類型到C++(傳遞簡單對象數據)

java native: 

  /**
     * 測試傳遞簡單對象
     *
     * @param simple
     * @return
     */
    public static native void testObj(Simple simple);

Simple類:

package com.sjl.jnidata;

/**
 * TODO
 *
 * @author Kelly
 * @version 1.0.0
 * @filename Simple.java
 * @time 2019/7/31 11:32
 * @copyright(C) 2019 song
 */
public class Simple {
    public Simple() {
    }

    /**
     * native層使用
     * @param a
     * @param b
     */
    public Simple(String a, int b) {
        this.a = a;
        this.b = b;
    }

    private String a;
    private int b;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    @Override
    public String toString() {
        return "Simple{" +
                "a='" + a + '\'' +
                ", b=" + b +
                '}';
    }
}

 C++實現:

/*
 * 傳遞簡單對象
 */
JNIEXPORT void JNICALL Java_com_sjl_jnidata_JniDataTransfer_testObj
        (JNIEnv *env, jclass clz, jobject obj) {
    jclass simple_cls = env->GetObjectClass(obj);

    if (simple_cls == NULL) {
        LOGW("GetObjectClass failed");
        return;
    }
    //獲得屬性字段
    jfieldID field_a = env->GetFieldID(simple_cls, "a", "Ljava/lang/String;");

    jfieldID field_b = env->GetFieldID(simple_cls, "b", "I");
    //獲得屬性值
    jstring a = (jstring) env->GetObjectField(obj, field_a);
    jint b = env->GetIntField(obj, field_b);
    const char *c_name = env->GetStringUTFChars(a, NULL);
    //釋放引用
    env->ReleaseStringUTFChars(a, c_name);
    LOGI("print simple obj:a=%s,b=%d", c_name, b);
}

 測試示例:

 Simple simple = new Simple();
        simple.setA("李四");
        simple.setB(99);
        JniDataTransfer.testObj(simple);

輸出結果:

 

5.java傳據遞引用數類型到C++(傳遞複雜對象數據)

java native: 

 /**
     * 測試傳遞複雜對象
     *
     * @param person
     * @return
     */
    public static native void testComplexObj(Person person);

Person類:

package com.sjl.jnidata;

import java.util.List;
import java.util.Map;

/**
 * TODO
 *
 * @author Kelly
 * @version 1.0.0
 * @filename Person.java
 * @time 2019/7/31 11:11
 * @copyright(C) 2019 song
 */
public class Person {
    private String name;//姓名
    private int age;//年齡
    private List<String> label;//個性標籤
    private List<Woman> womanList;//女人
    private Map<String,Woman> womanMap;//女人映射

    public Person() {
    }

    /***
     * native層使用
     * @param name
     * @param age
     * @param label
     * @param womanList
     * @param womanMap
     */
    public Person(String name, int age, List<String> label, List<Woman> womanList, Map<String, Woman> womanMap) {
        this.name = name;
        this.age = age;
        this.label = label;
        this.womanList = womanList;
        this.womanMap = womanMap;
    }

    public static class Woman {
        private String name;

        public Woman() {
        }

        public Woman(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Woman{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getLabel() {
        return label;
    }

    public void setLabel(List<String> label) {
        this.label = label;
    }

    public List<Woman> getWomanList() {
        return womanList;
    }

    public void setWomanList(List<Woman> womanList) {
        this.womanList = womanList;
    }

    public Map<String, Woman> getWomanMap() {
        return womanMap;
    }

    public void setWomanMap(Map<String, Woman> womanMap) {
        this.womanMap = womanMap;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", label=" + label +
                ", womanList=" + womanList +
                ", womanMap=" + womanMap +
                '}';
    }
}

C++實現:

/*
 *
 *
 * 傳遞複雜對象
 */
void JNICALL Java_com_sjl_jnidata_JniDataTransfer_testComplexObj
        (JNIEnv *env, jclass clz, jobject obj) {
    jclass complex_cls = env->GetObjectClass(obj);
    if (complex_cls == NULL) {
        LOGW("GetObjectClass failed");
        return;
    }
    //獲得屬性字段
    jfieldID field_name = env->GetFieldID(complex_cls, "name", "Ljava/lang/String;");
    jfieldID field_age = env->GetFieldID(complex_cls, "age", "I");
    jfieldID field_label = env->GetFieldID(complex_cls, "label", "Ljava/util/List;");
    jfieldID field_woman = env->GetFieldID(complex_cls, "womanList", "Ljava/util/List;");
    jfieldID field_womanMap = env->GetFieldID(complex_cls, "womanMap", "Ljava/util/Map;");

    //特別注意:如果java傳過來對象字段沒有賦值,Get***Field是空的

    //捕獲異常
    try {
        //獲得屬性值
        jstring name = (jstring) env->GetObjectField(obj, field_name);
        jint age = env->GetIntField(obj, field_age);
        const char *c_name = env->GetStringUTFChars(name, NULL);
        //釋放引用
        env->ReleaseStringUTFChars(name, c_name);

        LOGI("=========print complex obj start:");

        LOGI("name=%s,age=%d", c_name, age);

        //處理label List<String>
        jobject labelObj = (jobject) env->GetObjectField(obj, field_label);
        checkNull(labelObj, "labelObj is null.");
        //獲取List class
        jclass labelCls = env->GetObjectClass(labelObj);

        //獲取List的MethodID
        jmethodID label_get = env->GetMethodID(labelCls, "get", "(I)Ljava/lang/Object;");
        jmethodID label_size = env->GetMethodID(labelCls, "size", "()I");

        int labelSize = env->CallIntMethod(labelObj, label_size);
        for (int i = 0; i < labelSize; i++) {
            jobject label_obj = env->CallObjectMethod(labelObj, label_get, i);
            jstring str = (jstring) label_obj;

            const char *name = env->GetStringUTFChars(str, NULL);//拿到標籤名字
            if (name == NULL) {
                continue;
            }
            env->ReleaseStringUTFChars(str, name);
            LOGI("print label list 第%d個標籤:name=%s", i + 1, name);
        }

        //處理woman List<Woman>
        jobject womanObj = env->GetObjectField(obj, field_woman);
        checkNull(womanObj, "womanObj is null.");

        //獲取List class
        jclass womanCls = env->GetObjectClass(womanObj);

        //獲取List的MethodID
        jmethodID woman_get_id = env->GetMethodID(womanCls, "get", "(I)Ljava/lang/Object;");
        jmethodID woman_size_id = env->GetMethodID(womanCls, "size", "()I");

        int womanSize = env->CallIntMethod(womanObj, woman_size_id);
        for (int i = 0; i < womanSize; i++) {
            //通過get方法獲取
            jobject woman_obj = env->CallObjectMethod(womanObj, woman_get_id, i);
            jclass woman_cls = env->GetObjectClass(woman_obj);

            jmethodID nameId = env->GetMethodID(woman_cls, "getName", "()Ljava/lang/String;");
            jstring nameStr = (jstring) env->CallObjectMethod(woman_obj, nameId);
            const char *name = env->GetStringUTFChars(nameStr, NULL);//拿到名字
            if (name == NULL) {
                continue;
            }
            env->ReleaseStringUTFChars(nameStr, name);
            env->DeleteLocalRef(woman_obj);
            env->DeleteLocalRef(nameStr);
            LOGI("print woman list 第%d個女人:name=%s", i + 1, name);
        }

        jobject womanMapObj = env->GetObjectField(obj, field_womanMap);
        checkNull(womanMapObj, "womanMapObj is null.");
        //這樣也可以獲取
        jclass womanMapCls = env->FindClass("java/util/Map");
        jmethodID methodID_womanMap = env->GetMethodID(womanMapCls, "size", "()I");

        int womanMapSize = env->CallIntMethod(womanMapObj, methodID_womanMap);//map
        //使用iterator遍歷
        if (womanMapSize > 0) {
            jmethodID entrySetMID = env->GetMethodID(womanMapCls, "entrySet", "()Ljava/util/Set;");
            jobject setObj = env->CallObjectMethod(womanMapObj, entrySetMID);

            jclass setClass = env->FindClass("java/util/Set");

            jmethodID iteratorMID = env->GetMethodID(setClass, "iterator",
                                                     "()Ljava/util/Iterator;");
            jobject iteratorObj = env->CallObjectMethod(setObj, iteratorMID);
            jclass iteratorClz = env->FindClass("java/util/Iterator");
            jmethodID hasNextMID = env->GetMethodID(iteratorClz, "hasNext", "()Z");
            jmethodID nextMID = env->GetMethodID(iteratorClz, "next", "()Ljava/lang/Object;");
            //內部類使用$符號表示
            jclass entryClass = env->FindClass("java/util/Map$Entry");
            jmethodID getKeyMID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
            jmethodID getValueMID = env->GetMethodID(entryClass, "getValue",
                                                     "()Ljava/lang/Object;");
            while (env->CallBooleanMethod(iteratorObj, hasNextMID)) {
                jobject entryObj = env->CallObjectMethod(iteratorObj,
                                                         nextMID);//這個對應 Map.Entry<String, Person.Woman>
                jstring key = (jstring) env->CallObjectMethod(entryObj, getKeyMID);
                if (key == NULL) {
                    continue;
                }
                const char *keyStr = env->GetStringUTFChars(key, NULL);

                jobject woman_obj = env->CallObjectMethod(entryObj, getValueMID);
                if (woman_obj == NULL) {
                    continue;
                }
                jclass label_cls = env->GetObjectClass(woman_obj);

                jmethodID nameId = env->GetMethodID(label_cls, "getName", "()Ljava/lang/String;");
                jstring name = (jstring) env->CallObjectMethod(woman_obj, nameId);
                const char *nameStr = env->GetStringUTFChars(name, NULL);//拿到名字
                // 釋放UTF字符串資源
                env->ReleaseStringUTFChars(key, keyStr);
                env->ReleaseStringUTFChars(name, nameStr);
                // 釋放JNI局部引用資源
                env->DeleteLocalRef(entryObj);
                env->DeleteLocalRef(woman_obj);
                env->DeleteLocalRef(key);
                LOGI("print woman map 我與女人的映射關係:key=%s,name=%s", keyStr, nameStr);
            }
        }
        env->DeleteLocalRef(labelObj);
        env->DeleteLocalRef(womanObj);
        env->DeleteLocalRef(womanMapObj);
        LOGI("=========print complex obj end");
    } catch (NullException err) {
        LOGE("print complex obj error:%s", err.what());
    } catch (exception err) {//其它異常
        LOGE("print complex obj error:%s", err.what());
    }
}

細心的你可能會發現這與java的反射類似。 

  測試示例:

 Person person = new Person();
        person.setName("李四");
        person.setAge(99);

        List<String> label = Arrays.asList("唱", "跳", "rap", "籃球");
        List<Person.Woman> womanList = new ArrayList<>();
        Person.Woman woman1 = new Person.Woman();
        woman1.setName("恭喜");
        Person.Woman woman2 = new Person.Woman();
        woman2.setName("發財");
        womanList.add(woman1);
        womanList.add(woman2);
        Map<String, Person.Woman> womanMap = new HashMap<>();
        for (Person.Woman woman : womanList) {
            womanMap.put(person.getName() + ":" + woman.getName(), woman);
        }

        person.setLabel(label);
        person.setWomanList(womanList);
        person.setWomanMap(womanMap);
        JniDataTransfer.testComplexObj(person);

輸出結果: 

 

 6.java調用C++,native層操作java數據(返回簡單對象數據)

java native: 

 /**
     * 測試返回簡單對象
     *
     * @return
     */
    public static native Simple testGetObj();

C++實現:

/*
 *返回簡單對象
 */
JNIEXPORT jobject JNICALL Java_com_sjl_jnidata_JniDataTransfer_testGetObj
        (JNIEnv *env, jclass clz) {
    jclass simpleClz = env->FindClass("com/sjl/jnidata/Simple");
    //獲得構造函數,函數名爲 <init> 返回類型必須爲 void 即 V
    jmethodID constructorMID = env->GetMethodID(simpleClz, "<init>", "(Ljava/lang/String;I)V");
    char buff[100] = {0};
    char *pos = buff;
    int strLen = sprintf(pos, "%s:", "hello world from c++");
    pos += strLen;
    sprintf(pos, "%lli", getCurrentTime());
    jstring str = env->NewStringUTF(buff);
    jobject object = env->NewObject(simpleClz, constructorMID, str, 123456);
    env->DeleteLocalRef(str);
    return object;
}

 測試示例:

        Simple simple = JniDataTransfer.testGetObj();

輸出結果:  

 7.java調用C++,native層操作java數據(返回複雜對象數據)

java native: 

 /**
     * 測試返回複雜對象
     *
     * @return
     */
    public static native Person testGetComplexObj();

 C++實現:

/*
 * 返回複雜對象
 */
JNIEXPORT jobject JNICALL Java_com_sjl_jnidata_JniDataTransfer_testGetComplexObj
        (JNIEnv *env, jclass clz) {

    jclass personClz = env->FindClass("com/sjl/jnidata/Person");
    //獲得構造函數,函數名爲 <init> 返回類型必須爲 void 即 V
    //如果不通過構造器賦值,可以選擇set賦值
    jmethodID constructorMID = env->GetMethodID(personClz, "<init>",
                                                "(Ljava/lang/String;ILjava/util/List;Ljava/util/List;Ljava/util/Map;)V");
    jstring p_name = env->NewStringUTF("李四");
    jint p_age = 26;


    jclass list_cls = env->FindClass("java/util/ArrayList");

    jmethodID listConstructorMID = env->GetMethodID(list_cls, "<init>", "()V");

    //處理List<Label>
    jobject label_list_obj = env->NewObject(list_cls, listConstructorMID);
    jmethodID label_list_add = env->GetMethodID(list_cls, "add", "(Ljava/lang/Object;)Z");
    env->CallBooleanMethod(label_list_obj, label_list_add, env->NewStringUTF("唱"));
    env->CallBooleanMethod(label_list_obj, label_list_add, env->NewStringUTF("跳"));
    env->CallBooleanMethod(label_list_obj, label_list_add, env->NewStringUTF("rap"));
    env->CallBooleanMethod(label_list_obj, label_list_add, env->NewStringUTF("籃球"));

    //處理List<Woman>
    jobject woman_list_obj = env->NewObject(list_cls, listConstructorMID);
    jmethodID woman_list_add = env->GetMethodID(list_cls, "add", "(Ljava/lang/Object;)Z");

    jclass woman_cls = env->FindClass("com/sjl/jnidata/Person$Woman");//獲得Woman類
    jmethodID woman_constructor = env->GetMethodID(woman_cls, "<init>", "(Ljava/lang/String;)V");

    jstring woman1 = env->NewStringUTF("恭喜");
    jobject woman_obj1 = env->NewObject(woman_cls, woman_constructor, woman1);
    env->CallBooleanMethod(woman_list_obj, woman_list_add, woman_obj1);
    jstring woman2 = env->NewStringUTF("發財");
    jobject woman_obj2 = env->NewObject(woman_cls, woman_constructor, woman2);
    env->CallBooleanMethod(woman_list_obj, woman_list_add, woman_obj2);
    //處理Map<String,Woman>
    jclass map_cls = env->FindClass("java/util/HashMap");
    jmethodID mapConstructorMID = env->GetMethodID(map_cls, "<init>", "()V");
    jobject woman_map_obj = env->NewObject(map_cls, mapConstructorMID);
    jmethodID woman_map_put = env->GetMethodID(map_cls, "put",
                                               "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    env->CallObjectMethod(woman_map_obj, woman_map_put, env->NewStringUTF("李四:恭喜"), woman_obj1);
    env->CallObjectMethod(woman_map_obj, woman_map_put, env->NewStringUTF("李四:發財"), woman_obj2);

    jobject object = env->NewObject(personClz, constructorMID, p_name, p_age, label_list_obj,
                                    woman_list_obj, woman_map_obj);
    env->DeleteLocalRef(woman1);
    env->DeleteLocalRef(woman2);
    env->DeleteLocalRef(label_list_obj);
    env->DeleteLocalRef(woman_list_obj);
    env->DeleteLocalRef(woman_map_obj);
    return object;
}

測試示例:

        Person testGetComplexObj = JniDataTransfer.testGetComplexObj();

輸出結果:  

從上面可以看出C++層解析java對象比構建java對象稍微複雜一些,總之,實現思路和java思想一致。 

 8.java調用C++,native層操作java數據(C++對java對象變量設置值)

java native: 

  private String name = "java";

    /**
     * C++賦值java變量
     */
    public native void setFieldValue();
    

    public String getName() {
        return name;
    }

 C++實現:


/*
 * C++修改java字段值
 */
JNIEXPORT void JNICALL Java_com_sjl_jnidata_JniDataTransfer_setFieldValue
        (JNIEnv *env, jobject obj) {//如果是靜態方法,第二個參數是class
    jclass cls = env->GetObjectClass(obj);
    jfieldID nameFieldId = env->GetFieldID(cls, "name", "Ljava/lang/String;");
    char new_name[] = "C++設置java字段值成功";
    jstring cName = env->NewStringUTF(new_name);
    env->SetObjectField(obj, nameFieldId, cName);//修改對象實例值
}

 測試示例:

 JniDataTransfer jniDataTransfer = new JniDataTransfer();
        String name1 = jniDataTransfer.getName();
        jniDataTransfer.setFieldValue();
        String name2 = jniDataTransfer.getName();

輸出結果:  

 9.java調用C++,native層操作java數據(C++回調java接口方法)

java native: 

  private JniDataListener jniDataListener;

    /**
     * 回調成功還是異常控制標誌
     *
     * @param flag
     */
    public native void setFlag(boolean flag);

    /**
     * C++回調java成功
     */
    public void callSuccess(String msg) {
        if (jniDataListener != null) {
            jniDataListener.onSuccess(msg);
        }
    }

    /**
     * C++回調java異常
     */
    public void callError(Exception e) {
        if (jniDataListener != null) {
            jniDataListener.onError(e);
        }
    }


    public interface JniDataListener {
        void onSuccess(String msg);

        void onError(Exception e);
    }

    public void setJniDataListener(JniDataListener jniDataListener) {
        this.jniDataListener = jniDataListener;
    }

 C++實現:

/*
 * C++回調java
 */
JNIEXPORT void JNICALL Java_com_sjl_jnidata_JniDataTransfer_setFlag
        (JNIEnv *env, jobject obj, jboolean b) {
    jclass cls = env->GetObjectClass(obj);
    jmethodID callSuccessFieldId = env->GetMethodID(cls, "callSuccess", "(Ljava/lang/String;)V");
    jmethodID callErrorFieldId = env->GetMethodID(cls, "callError", "(Ljava/lang/Exception;)V");
    if (b) {
        jstring msg = env->NewStringUTF("來自C++回調,成功");
        //回調成功方法,並傳遞參數
        env->CallVoidMethod(obj , callSuccessFieldId , msg);
    } else {
        jstring msg = env->NewStringUTF("來自C++回調,異常");
        jclass exceptionClz = env->FindClass("java/lang/Exception");
        jmethodID constructorMID = env->GetMethodID(exceptionClz, "<init>", "(Ljava/lang/String;)V");
        jobject object = env->NewObject(exceptionClz, constructorMID, msg);
        env->CallVoidMethod(obj, callErrorFieldId , object);
    }
}

 測試示例:

 boolean callbackFlag = true;

   public void callbackJava(View view) {
        JniDataTransfer jniDataTransfer = new JniDataTransfer();
        //設置監聽
        jniDataTransfer.setJniDataListener(new JniDataTransfer.JniDataListener() {
            @Override
            public void onSuccess(String msg) {
                showMsg("回調成功:" + msg + "");
                callbackFlag = false;
            }

            @Override
            public void onError(Exception e) {
                showMsg("回調異常:" + e.getMessage());
                callbackFlag = true;
            }
        });

        jniDataTransfer.setFlag(callbackFlag);

    }

demo地址 

 https://github.com/kellysong/jni-data-transfer 

總結

         本文章舉例了多種jni數據傳遞方式,實際開發中涉及業務邏輯,更復雜,開發中需要注意java類包名和方法簽名是否正確,避免出現比必要的麻煩,希望這篇文章能夠幫助有jni開發需求的人。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章