(三)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

 

posted on 2018-03-28 10:34  maogefff  阅读(441)  评论(0编辑  收藏  举报

导航