前言
這次總結一下jni數據交互(通訊)的方式,本篇側重應用,native層主要用C++編寫。掌握數據交互方式,對入門jni及理解java與C++的數據類型映射起到四兩撥千斤的作用。本編着重jni數據傳遞實現,跳過jni開發環境搭建及jni基礎知識(網上基本可以找到對應解決方案)。
平時爲了實現某個功能(RFID編解碼庫),如果C++開發那邊已經有現成的C++類庫,再用java進行重複的工作就沒必要了。我們只需提供接口java層調用即可,如果C++大佬沒空,接口層代碼自己也可以實現,最後在編譯成Android平臺調用的so庫。so庫可以自己使用,也可以給客戶使用,不用擔心代碼被反編譯的問題。
本次實現jni數據交互方式(jni靜態註冊)如圖所示:
jni編譯的方式
- 使用Cmake文件編譯
- 使用mk文件編譯(這次使用mk文件編譯)
環境(知識jni開發的環境)準備:
這兩種方式都可以編譯調試 ,如果只是調試native與java層代碼,是不需要執行ndk-build命令(生成不同cpu架構的so庫)。
jni開發流程
- 編寫java本地方法
- 生成.h頭文件 , 使用java命令是javah(必須在包名外使用javah命令)
- 創建jni目錄,引入頭文件,根據頭文件實現C/C++代碼
- 編寫Android.mk、Application.mk文件
- Ndk編譯生成動態庫
- 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開發需求的人。