• 深入理解JNINativeInterface函数<三>


    本篇着重点是异常、字符串和数组的操作处理

    一、异常

        jint        (*Throw)(JNIEnv*, jthrowable);
        jint        (*ThrowNew)(JNIEnv *, jclass, const char *);
        jthrowable  (*ExceptionOccurred)(JNIEnv*);
        jboolean    (*ExceptionCheck)(JNIEnv*);
        void        (*ExceptionDescribe)(JNIEnv*);
        void        (*ExceptionClear)(JNIEnv*);
        void        (*FatalError)(JNIEnv*, const char*);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1、检查异常

     jthrowable  (*ExceptionOccurred)(JNIEnv*);
     jboolean    (*ExceptionCheck)(JNIEnv*);
    
    • 1
    • 2

    调用了JNI的ExceptionCheck函数检查最近一次JNI调用是否发生了异常,如果有异常这个函数返回JNI_TRUE,否则返回JNI_FALSE

    异常检查JNI还提供了另外一个接口,ExceptionOccurred,如果检测有异常发生时,该函数会返回一个指向当前异常的引用。作用和ExceptionCheck一样,两者的区别在于返回值不一样

    2、抛出异常给上层

        jint        (*Throw)(JNIEnv*, jthrowable);
        jint        (*ThrowNew)(JNIEnv *, jclass, const char *);
    
    • 1
    • 2

    Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常
    ThrowNew:在当前线程触发一个异常,并自定义输出异常信息

    3、打印异常堆栈信息

     void        (*ExceptionDescribe)(JNIEnv*);
    
    • 1

    4、清除异常

     void        (*ExceptionClear)(JNIEnv*);
    
    • 1

    示例

    JavaClass.java

    package com.anniljing.jnidemo;
    
    import android.util.Log;
    
    public class JavaClass {
        public static String name;
        private String version = "hello";
        private static int sCode;
    
        public JavaClass() {
            Log.d("MainActivity", "Init JavaClass");
        }
    
        public String getVersion() {
            return version;
        }
    
        public void setVersion(String version) {
            this.version = version;
        }
    
        public static void setCode(int code) {
            sCode = code;
        }
    
        @Override
        public String toString() {
            return "JavaClass{" +
                    "version='" + version + '\'' + "Code=" + sCode + '\'' +
                    '}';
        }
    
        public void getResulte() {
            int data[]=new int[2];
            data[3]=5;
            Log.d("JavaClass", "getResulte");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    HandleException.java

    package com.anniljing.jnidemo.ExceptionHandle;
    
    public class HandleException {
        public static native void handleJniExeption();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    HandleException.cpp

    #include 
    #include 
    #include 
    #include 
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_anniljing_jnidemo_ExceptionHandle_HandleException_handleJniExeption(JNIEnv *env,
                                                                                 jclass clazz) {
        //向 Java 层抛出异常
        jclass exceptionClz = env->FindClass("com/anniljing/jnidemo/JavaClass");
        jmethodID constructure = env->GetMethodID(exceptionClz, "", "()V");
        jobject obj = env->NewObject(exceptionClz, constructure);
        jmethodID getResulteID = env->GetMethodID(exceptionClz, "getResulte", "()V");
        env->CallVoidMethod(obj, getResulteID);
        //检查当前环境是否发生异常(类似于 Java try{})
        if (env->ExceptionCheck()) {
            // 处理异常(类似于 Java 的 catch{})
            env->ExceptionDescribe();
            LOGD("exception from native");
        }
        //清除异常
        env->ExceptionClear();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    二、字符串操作

        jstring     (*NewString)(JNIEnv*, const jchar*, jsize);
        jsize       (*GetStringLength)(JNIEnv*, jstring);
        const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
        void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
        
        jstring     (*NewStringUTF)(JNIEnv*, const char*);
        jsize       (*GetStringUTFLength)(JNIEnv*, jstring);
        const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
        void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1、构造新的java.lang.String对象

     jstring     (*NewString)(JNIEnv*, const jchar*, jsize);
     jstring     (*NewStringUTF)(JNIEnv*, const char*);
    
    • 1
    • 2

    2、获取字符串的长度

     jsize       (*GetStringLength)(JNIEnv*, jstring);
     jsize       (*GetStringUTFLength)(JNIEnv*, jstring);
    
    • 1
    • 2

    3、获取字符串的数组指针

    const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
    const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
    
    • 1
    • 2

    4、通知 VM 本机代码不再需要访问

    void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
    void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
    
    • 1
    • 2

    示例

    StringHandle.java

    package com.anniljing.jnidemo.StringHandle;
    
    public class StringHandle {
        public static native String stringUTFHandle();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    StringHandle.cpp

    #include 
    #include 
    #include 
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_anniljing_jnidemo_StringHandle_StringHandle_stringUTFHandle(JNIEnv *env, jclass clazz) {
        jstring newNativeString = env->NewStringUTF("Hello string from native");
        //定义一个const char数组
        const char *_str = NULL;
        jsize stringSize = env->GetStringUTFLength(newNativeString);
        LOGD("String size is:%d", stringSize);
        jboolean isCopy = false;
        //将java字符串转为UTF-8格式的const char数组
        _str = env->GetStringUTFChars(newNativeString, &isCopy);
        LOGD("GetStringUTFChars is:%s", _str);
        env->ReleaseStringUTFChars(newNativeString, _str);
        //在栈上分配buffer,不需要手动释放,函数结束自动释放
        char inputbuf[128];
        env->GetStringUTFRegion(newNativeString, 0, 6, inputbuf);
        LOGD("GetStringUTFRegion is:%s", inputbuf);
        //在自由存储区分配buffer空间。自由存储区不仅可以是堆,还可以是静态存储区
        char *p = new char[128];
        env->GetStringUTFRegion(newNativeString, 0, 6, p);
        LOGD("GetStringUTFRegion with new:%s", p);
        //释放自由存储区空间,与new[]关键字成对使用
        delete[] p;
    
        //在堆分配buffer空间
        char *ptr = (char *) malloc(sizeof(char) * 6);
        env->GetStringUTFRegion(newNativeString, 0, 6, ptr);
        LOGD("GetStringUTFRegion with malloc:%s", ptr);
        //释放堆空间,与malloc函数成对使用
        free(ptr);
    
        //释放const char数组
        env->ReleaseStringUTFChars(newNativeString, _str);
        return newNativeString;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    在这里插入图片描述

    三、数组相关的操作

    1、基本数据类型的数组操作

    1.1、创建新的数组

        jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
        jbyteArray    (*NewByteArray)(JNIEnv*, jsize);
        jcharArray    (*NewCharArray)(JNIEnv*, jsize);
        jshortArray   (*NewShortArray)(JNIEnv*, jsize);
        jintArray     (*NewIntArray)(JNIEnv*, jsize);
        jlongArray    (*NewLongArray)(JNIEnv*, jsize);
        jfloatArray   (*NewFloatArray)(JNIEnv*, jsize);
        jdoubleArray  (*NewDoubleArray)(JNIEnv*, jsize);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.2、获取数组的元素

        jboolean*   (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
        jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
        jchar*      (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
        jshort*     (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
        jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
        jlong*      (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
        jfloat*     (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
        jdouble*    (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.3、获取指定范围的数组

        void        (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,jsize, jsize, jboolean*);
        void        (*GetByteArrayRegion)(JNIEnv*, jbyteArray,jsize, jsize, jbyte*);
        void        (*GetCharArrayRegion)(JNIEnv*, jcharArray,jsize, jsize, jchar*);
        void        (*GetShortArrayRegion)(JNIEnv*, jshortArray,jsize, jsize, jshort*);
        void        (*GetIntArrayRegion)(JNIEnv*, jintArray,jsize, jsize, jint*);
        void        (*GetLongArrayRegion)(JNIEnv*, jlongArray,jsize, jsize, jlong*);
        void        (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,jsize, jsize, jfloat*);
        void        (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,jsize, jsize, jdouble*);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.4、设置指定范围的数组

        void        (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,jsize, jsize, const jboolean*);
        void        (*SetByteArrayRegion)(JNIEnv*, jbyteArray,jsize, jsize, const jbyte*);
        void        (*SetCharArrayRegion)(JNIEnv*, jcharArray,jsize, jsize, const jchar*);
        void        (*SetShortArrayRegion)(JNIEnv*, jshortArray,jsize, jsize, const jshort*);
        void        (*SetIntArrayRegion)(JNIEnv*, jintArray,jsize, jsize, const jint*);
        void        (*SetLongArrayRegion)(JNIEnv*, jlongArray,jsize, jsize, const jlong*);
        void        (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,jsize, jsize, const jfloat*);
        void        (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,jsize, jsize, const jdouble*);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.5、native层释放数组资源

        void        (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,jboolean*, jint);
        void        (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,jbyte*, jint);
        void        (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,jchar*, jint);
        void        (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,jshort*, jint);
        void        (*ReleaseIntArrayElements)(JNIEnv*, jintArray,jint*, jint);
        void        (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,jlong*, jint);
        void        (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,jfloat*, jint);
        void        (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,jdouble*, jint);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例

    ArrayHandle.java

    package com.anniljing.jnidemo.ArrayHandle;
    
    public class ArrayHandle {
        //从native层返回int类型的数组
        public static native int[] getArrayData();
        //java层传入native层,native处理以后,返回新的数组
        public static native int[] handleJavaArray(int[] srcData);
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ArrayHandle.cpp

    #include 
    #include 
    #include 
    
    extern "C"
    JNIEXPORT jintArray JNICALL
    Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_getArrayData(JNIEnv *env, jclass clazz) {
        jintArray intArray = env->NewIntArray(10);
        //通过GetIntArrayElements拿到C类型的数组的指针,然后才能进行C数组的操作
        jint *element = env->GetIntArrayElements(intArray, NULL);
        LOGD("Before GetIntArrayElements element[0]:%d", *element);
        jint len = env->GetArrayLength(intArray);
        for (int i = 0; i < len; ++i) {
            *(element + i) = (i * 10 + 1);
            LOGD("For intArray:address(%p),value(%d)", (element + i), *(element + i));
            LOGD("elment address:%p", element);
        }
        env->ReleaseIntArrayElements(intArray, element, 0);
        return intArray;
    }
    extern "C"
    JNIEXPORT jintArray JNICALL
    Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_handleJavaArray(JNIEnv *env, jclass clazz,
                                                                       jintArray java_data) {
        jintArray result = env->NewIntArray(2);
        jint buf[2];
        //将java_data数据复制到buf里面
        env->GetIntArrayRegion(java_data, 0, 2, buf);
        for (int i = 0; i < 2; ++i) {
            LOGD("buf data:%d", buf[i]);
        }
        //将buf数据复制到result里面
        env->SetIntArrayRegion(result, 0, 2, buf);
        return result;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    MainActivity.java

     public void handleArray(View view) {
            int[] arrayData = ArrayHandle.getArrayData();
            for (int i = 0; i < arrayData.length; i++) {
                Log.d(TAG, "Native生成的数据:" + arrayData[i]);
            }
    
            int[] javaData = {1, 3, 5, 7, 9};
            int[] destData = ArrayHandle.handleJavaArray(javaData);
            for (int i = 0; i < destData.length; i++) {
                Log.d(TAG, "Native转换后的数据:" + destData[i]);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    2、String以及object类型数组的操作

    2.1、获取数组下标元素

     jobject     (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
    
    • 1

    2.2、设置指定下标元素

    void        (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
    
    • 1

    2.3、获取数组长度

    jsize       (*GetArrayLength)(JNIEnv*, jarray);
    
    • 1

    示例

    JavaClass.java

    package com.anniljing.jnidemo;
    
    import android.util.Log;
    
    public class JavaClass {
        public static String name;
        private String version = "hello";
        private static int sCode;
    
        public String getVersion() {
            return version;
        }
    
        public void setVersion(String version) {
            this.version = version;
        }
    
        public static void setCode(int code) {
            sCode = code;
        }
    
        @Override
        public String toString() {
            return "JavaClass{" +
                    "version='" + version + '\'' + "Code=" + sCode + '\'' +
                    '}';
        }
    
        public void getResulte() {
            int data[]=new int[2];
            data[3]=5;
            Log.d("JavaClass", "getResulte");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    ArrayHandle.java

    package com.anniljing.jnidemo.ArrayHandle;
    
    public class ArrayHandle {
        //处理String类型的数组
        public static native String[] handleStringArray(String[] srcData);
        //处理Object类型的数组
        public static native Object[] handleObjectArray(Object[] objects);
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ArrayHandle.cpp

    #include 
    #include 
    #include 
    #include 
    
    extern "C"
    JNIEXPORT jobjectArray JNICALL
    Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_handleStringArray(JNIEnv *env, jclass clazz,
                                                                         jobjectArray src_data) {
        jint len = env->GetArrayLength(src_data);
        jboolean isCopy = false;
        for (int i = 0; i < len; ++i) {
            const char *content = NULL;
            jstring element = static_cast<jstring>(env->GetObjectArrayElement(src_data, i));
            content = env->GetStringUTFChars(element, &isCopy);
            const size_t contentLen = strlen(content);
            char *replace = new char[contentLen + 1];
            strncpy(replace, content, contentLen);
            memcpy(replace, "李", strlen("李"));
            jstring last = env->NewStringUTF(replace);
            env->SetObjectArrayElement(src_data, i, last);
            LOGD("Replace content:%s", replace);
            delete[] replace;
        }
        return src_data;
    }
    
    extern "C"
    JNIEXPORT jobjectArray JNICALL
    Java_com_anniljing_jnidemo_ArrayHandle_ArrayHandle_handleObjectArray(JNIEnv *env, jclass clazz,
                                                                         jobjectArray objects) {
        jclass javaClass = env->FindClass("com/anniljing/jnidemo/JavaClass");
        int len = env->GetArrayLength(objects);
        for (int i = 0; i < len; ++i) {
            jobject javaJObject = env->GetObjectArrayElement(objects, i);
            jfieldID versionField = env->GetFieldID(javaClass, "version", "Ljava/lang/String;");
            jstring versionValue = static_cast<jstring>(env->GetObjectField(javaJObject, versionField));
            const char *_version = NULL;
            _version = env->GetStringUTFChars(versionValue, NULL);
            LOGD("%s", _version);
            char *nativeVersion = new char[30];
            strcpy(nativeVersion, "Native version v1.0.");
            char *index = new char[10];
            sprintf(index, "%d", i);
            strcat(nativeVersion, index);
    
            jmethodID setVersion = env->GetMethodID(javaClass, "setVersion", "(Ljava/lang/String;)V");
            env->CallVoidMethod(javaJObject, setVersion, env->NewStringUTF(nativeVersion));
            env->SetObjectArrayElement(objects, i, javaJObject);
        }
    
        return objects;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    MainActivity.java

    public void handleArray(View view) {
            String[] names = new String[4];
            names[0] = "张三";
            names[1] = "张四";
            names[2] = "张五";
            names[3] = "张六";
            try {
                ArrayHandle.handleStringArray(names);
            } catch (Exception e) {
                e.printStackTrace();
            }
            for (int i = 0; i < names.length; i++) {
                Log.d(TAG, names[i]);
            }
    
            JavaClass[] javaClasses = new JavaClass[10];
            int len = javaClasses.length;
            for (int i = 0; i < len; i++) {
                JavaClass javaClass = new JavaClass();
                javaClass.setVersion("java version:1.0." + i);
                javaClasses[i] = javaClass;
            }
            JavaClass[] objects = (JavaClass[]) ArrayHandle.handleObjectArray(javaClasses);
            for (int i = 0; i < objects.length; i++) {
                Log.d(TAG, objects[i].getVersion());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    源码已同步gitHub:https://github.com/AinialJing/JniDemo

  • 相关阅读:
    高等数学(第七版)同济大学 习题9-6 个人解答
    如何在Mac上恢复已删除的文件?
    【计算机体系知识】操作系统笔记(持续更新)
    OpenCV(三十四):轮廓外接最大、最小矩形和多边形拟合
    C++ primer 查漏补缺三 :初始化
    typescript:命名空间
    java游戏制作-拼图游戏
    HTML选项框的设计以及根据不同选项的值对应不同的事件
    Python图像处理丨图像的灰度线性变换
    批量挖漏洞(从内网到外网、从白盒到黑盒)
  • 原文地址:https://blog.csdn.net/u011557841/article/details/126371761