(三)JNI常用示例
针对我之前文章的练习:JNI方法总结
1. 字符串
JAVA层:
test.testString("HELLOWORLD");
JNI层:
JNIEXPORT jstring JNICALL Java_com_aplex_canopen_CANopen_testString(JNIEnv *env, jobject obj, jstring str){ jboolean isCopy; const char *cstr = env->GetStringUTFChars(str, &isCopy); //这个是把android默认的Unicode编码转换为UTF-8编码,然后得到数据 if(isCopy==JNI_FALSE){ LOGD("错误"); } else if(isCopy==JNI_TRUE){ LOGD("正确"); } LOGD("UTF-8数据为:%s", cstr); LOGD("UTF-8数据长度为:%d", env->GetStringUTFLength(str)); LOGD("Unicode数据长度为:%d", env->GetStringLength(str)); env->ReleaseStringUTFChars(str, cstr); std::string strin = "hello world"; return env->NewStringUTF(strin.c_str()); //生成一个新的UTF-8格式字符串 }
结果:
D/Canopencommand: 正确 D/Canopencommand: UTF-8数据为:HELLOWORLD D/Canopencommand: UTF-8数据长度为:10 D/Canopencommand: Unicode数据长度为:10
2. 基本类型数组
java层:
test.testArray(new int[]{1,2,3,4,5,6,7,8,9});
JNI层:
JNIEXPORT void JNICALL Java_com_aplex_canopen_CANopen_testArray (JNIEnv *env, jobject obj, jintArray arr){ jboolean isCopy; LOGD("######基本类型数组测试#########"); //1. 拿到数组长度 jint len = env->GetArrayLength(arr); LOGD("数组长度为:%d",len); //2. 拿到数组方法一:也就是转成可用的int* jint* val = env->GetIntArrayElements(arr, &isCopy); if(isCopy){ LOGD("数组元素为"); for (int i = 0; i < len; ++i) { LOGD("元素%d:%d",i,val[i]); } } //3. 拿到数组方法二:拿指定长度数组 jint val1[len]; env->GetIntArrayRegion(arr, 0, len, val1); //0:起始索引; len: 长度; buf:需要放入的地方 LOGD("数组元素1为"); for (int i = 0; i < len; ++i) { LOGD("元素%d:%d",i,val1[i]); } }
结果
D/Canopencommand: ######基本类型数组测试######### D/Canopencommand: 数组长度为:9 D/Canopencommand: 数组元素为 D/Canopencommand: 元素0:1 D/Canopencommand: 元素1:2 D/Canopencommand: 元素2:3 D/Canopencommand: 元素3:4 D/Canopencommand: 元素4:5 D/Canopencommand: 元素5:6 D/Canopencommand: 元素6:7 D/Canopencommand: 元素7:8 D/Canopencommand: 元素8:9 D/Canopencommand: 数组元素1为 D/Canopencommand: 元素0:1 D/Canopencommand: 元素1:2 D/Canopencommand: 元素2:3 D/Canopencommand: 元素3:4 D/Canopencommand: 元素4:5 D/Canopencommand: 元素5:6 D/Canopencommand: 元素6:7 D/Canopencommand: 元素7:8 D/Canopencommand: 元素8:9
3. 对象类型数组
JAVA层:
test.testArrayObject(new String[]{"haha","hehe","wawa","dada"});
JNI层:
JNIEXPORT void JNICALL Java_com_aplex_canopen_CANopen_testArrayObject (JNIEnv *env, jobject obj, jobjectArray strArr){ jboolean isCopy; LOGD("######对象类型数组测试#########"); int len = env->GetArrayLength(strArr); //拿到数组长度 //1. 拿到对象数组数据 for (int i = 0; i < len; ++i) { jstring str = (jstring)env->GetObjectArrayElement(strArr, i); //拿到对象数组的元素:注意要强制转换 const char *cstr = env->GetStringUTFChars(str, &isCopy); LOGD("字符串%d为:%s",i,cstr); env->ReleaseStringUTFChars(str, cstr); } //2. 设置对象数组数据 jstring str = env->NewStringUTF("abcdefg"); env->SetObjectArrayElement(strArr, 0, str); //修改对象数组中的数据 LOGD("修改后再看一遍2"); for (int i = 0; i < len; ++i) { jstring str = (jstring)env->GetObjectArrayElement(strArr, i); const char *cstr = env->GetStringUTFChars(str, &isCopy); LOGD("字符串%d为:%s",i,cstr); env->ReleaseStringUTFChars(str, cstr); } //3. 构造一个新的对象数组 jclass strClass = env->FindClass("java/lang/String"); jobjectArray newStr = env->NewObjectArray(len-1, strClass, 0); //创建一个新的对象数组 len = env->GetArrayLength(newStr); //拿到新创立数组的长度 for (int i = 0; i < len; ++i) { jstring str = (jstring)env->GetObjectArrayElement(strArr, i); //拿到对象数组的元素:注意要强制转换 env->SetObjectArrayElement(newStr, i, str); //修改对象数组中的数据 } LOGD("修改后再看一遍3"); for (int i = 0; i < len; ++i) { jstring str = (jstring)env->GetObjectArrayElement(newStr, i); const char *cstr = env->GetStringUTFChars(str, &isCopy); LOGD("字符串%d为:%s",i,cstr); env->ReleaseStringUTFChars(str, cstr); } }
结果:
######对象类型数组测试######### D/Canopencommand: 字符串0为:haha D/Canopencommand: 字符串1为:hehe D/Canopencommand: 字符串2为:wawa D/Canopencommand: 字符串3为:dada D/Canopencommand: 修改后再看一遍2 D/Canopencommand: 字符串0为:abcdefg D/Canopencommand: 字符串1为:hehe D/Canopencommand: 字符串2为:wawa D/Canopencommand: 字符串3为:dada D/Canopencommand: 修改后再看一遍3 D/Canopencommand: 字符串0为:abcdefg D/Canopencommand: 字符串1为:hehe D/Canopencommand: 字符串2为:wawa
4. 在JNI中调用实例化对象中的方法1:(使用自身对象,自身已被实例化)
JAVA层:测试对象
public class CANopen { private static final String TAG = "CANopen"; static { Log.d(TAG, "static1"); System.loadLibrary("Canopen-lib"); Log.d(TAG, "static2"); } public native void testCallMethod(); private void JNIcallback(){ Log.d(TAG, "这是JNI从C/C++回调JAVA实例化对象中的方法"); } private int JNIcallback2(int a, byte b){ Log.d(TAG, "整型="+a+";字节="+b); return 3; } private String JNIcallback3(int a, String b, byte[]c){ Log.d(TAG, "整型="+a); Log.d(TAG,"字符串="+b); Log.d(TAG,"数组长度="+c.length); return new String("hello world"); } }
JAVA层:
Log.d(TAG, "onCreate1"); caNopen.testCallMethod(); Log.d(TAG, "onCreate2");
JNI层:
JNIEXPORT void JNICALL Java_com_aplex_canopen_CANopen_testCallMethod (JNIEnv *env, jobject obj){ jboolean isCopy; //1. 先调用一个比较简单的(无参无返回) LOGD("##########第一个############"); jclass jc = env->GetObjectClass(obj); //通过实例化对象找到类 jmethodID id = env->GetMethodID(jc, "JNIcallback", "()V"); //找到实例化对象里函数的ID env->CallVoidMethod(obj, id); //调用obj这个实例化对象里的函数id //2. 调用稍微复杂点的(基本类型参数和返回值) LOGD("##########第二个############"); jclass jc2 = env->GetObjectClass(obj); //通过实例化对象找到类 jmethodID id2 = env->GetMethodID(jc2, "JNIcallback2", "(IB)I"); //找到实例化对象里函数的ID jint i = env->CallIntMethod(obj, id2, 0x1234, 5); //调用obj这个实例化对象里的函数id LOGD("JNI中返回参数=%d",i); //3. 调用一个比较复杂的(带对象类型参数和返回值) LOGD("##########第三个############"); jclass jc3 = env->GetObjectClass(obj); //通过实例化对象找到类 jmethodID id3 = env->GetMethodID(jc3, "JNIcallback3", "(ILjava/lang/String;[B)Ljava/lang/String;"); //找到实例化对象里函数的ID //创建byte数组,注意:无法赋值!!! jbyteArray byteArray = env->NewByteArray(45); int len = env->GetArrayLength(byteArray); //创建一个string并赋值 jstring jstr = env->NewStringUTF("fuck!!"); jstring str = (jstring)env->CallObjectMethod(obj, id3, 12, jstr,byteArray); //调用obj2这个实例化对象里的函数id,返回值是对象,要进行强制转换 const char* cstr = env->GetStringUTFChars(str,&isCopy); LOGD("JNI中打印:%s", cstr); env->ReleaseStringUTFChars(str, cstr); }
结果:
D/MainActivity: onCreate1 D/Canopencommand: ##########第一个############ D/CANopen: 这是JNI从C/C++回调JAVA实例化对象中的方法 D/Canopencommand: ##########第二个############ D/CANopen: 整型=4660;字节=5 D/Canopencommand: JNI中返回参数=3 D/Canopencommand: ##########第三个############ D/CANopen: 整型=12 D/CANopen: 字符串=fuck!! D/CANopen: 数组长度=45 D/Canopencommand: JNI中打印:hello world D/MainActivity: onCreate2
5. 在JNI中调用实例化对象中的方法2:(新建实例化对象,修改对象的属性值,调用对象方法)
需要实例化的对象:
package com.aplex.canopen; import android.util.Log; public class TestClass { private String TAG = "TestClass"; private int a; TestClass(){ a = 1; Log.d(TAG,"调用无参构造函数"); } TestClass(int a){ this.a = a; Log.d(TAG,"调用有参构造函数="+a); } public int getValue(){ Log.d(TAG, "a="+this.a); return this.a; } }
JAVA层:
Log.d(TAG, "onCreate1"); caNopen.testCallMethod2(); Log.d(TAG, "onCreate2");
JNI层:
JNIEXPORT void JNICALL Java_com_aplex_canopen_CANopen_testCallMethod2 (JNIEnv *env, jobject obj){ //1. 调用无参构造函数、修改变量(属性)值 LOGD("##########调用无参构造函数############"); jclass jc = env->FindClass("com/aplex/canopen/TestClass"); //找到类,该类未被实例化 jmethodID initID = env->GetMethodID(jc, "<init>", "()V");//找到构造函数的id;注意:构造函数"<init>"为固定值 jobject mobj = env->NewObject(jc, initID); //实例化一个对象 jmethodID funcID = env->GetMethodID(jc, "getValue", "()I"); //拿到方法的ID jint i = env->CallIntMethod(mobj, funcID); //调用方法 LOGD("修改前查看函数返回的a=%d",i); jfieldID fieldID = env->GetFieldID(jc, "a", "I"); //拿到变量(属性)的ID jint ii = env->GetIntField(mobj, fieldID); LOGD("查看拿到的值a=%d",ii); ii = 100; env->SetIntField(mobj, fieldID, ii); jint iii = env->CallIntMethod(mobj, funcID); //再次调用查看是否修改属性值 LOGD("修改后查看函数返回的a=%d",iii); //2. 调用有参构造函数 LOGD("##########调用有参构造函数############"); jclass jc2 = env->FindClass("com/aplex/canopen/TestClass"); jmethodID initID2 = env->GetMethodID(jc2, "<init>", "(I)V"); //注意这里:"(I)V" jobject mobj2 = env->NewObject(jc2, initID2, 123); jmethodID funcID2 = env->GetMethodID(jc2, "getValue", "()I"); jint i2 = env->CallIntMethod(mobj2, funcID2); LOGD("a=%d",i2); }
结果:
D/MainActivity: onCreate1 D/Canopencommand: ##########调用无参构造函数############ D/TestClass: 调用无参构造函数 D/TestClass: a=1 D/Canopencommand: 修改前查看函数返回的a=1 D/Canopencommand: 查看拿到的值a=1 D/TestClass: a=100 D/Canopencommand: 修改后查看函数返回的a=100 D/Canopencommand: ##########调用有参构造函数############ D/TestClass: 调用有参构造函数=123 D/TestClass: a=123 D/Canopencommand: a=123 D/MainActivity: onCreate2
6. 异常处理
JAVA层:
caNopen.testException();
JNI层:
JNIEXPORT void JNICALL Java_com_aplex_canopen_CANopen_testException (JNIEnv *env, jobject obj){ jclass jc = env->FindClass("java/io/IOException"); env->ThrowNew(jc, "异常测试"); }
结果:
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.aplex.canopen, PID: 20296 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.aplex.canopen/com.aplex.canopen.MainActivity}: java.io.IOException: 异常测试 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5422) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: java.io.IOException: 异常测试 at com.aplex.canopen.CANopen.testException(Native Method) at com.aplex.canopen.MainActivity.onCreate(MainActivity.java:46) at android.app.Activity.performCreate(Activity.java:6251) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5422) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)