Android JNI 深入理解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、检查异常

 jthrowable  (*ExceptionOccurred)(JNIEnv*);
 jboolean    (*ExceptionCheck)(JNIEnv*);

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

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

2、抛出异常给上层

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

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

3、打印异常堆栈信息

 void        (*ExceptionDescribe)(JNIEnv*);

4、清除异常

 void        (*ExceptionClear)(JNIEnv*);

示例

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");
    }
}

HandleException.java

package com.anniljing.jnidemo.ExceptionHandle;

public class HandleException {
    public static native void handleJniExeption();
}

HandleException.cpp

#include <jni.h>
#include <AndroidLog.h>
#include <jni.h>
#include <jni.h>

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, "<init>", "()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();
}

在这里插入图片描述

二、字符串操作

    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、构造新的java.lang.String对象

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

2、获取字符串的长度

 jsize       (*GetStringLength)(JNIEnv*, jstring);
 jsize       (*GetStringUTFLength)(JNIEnv*, jstring);

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

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

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

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

示例

StringHandle.java

package com.anniljing.jnidemo.StringHandle;

public class StringHandle {
    public static native String stringUTFHandle();
}

StringHandle.cpp

#include <jni.h>
#include <AndroidLog.h>
#include <cstdlib>
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、基本数据类型的数组操作

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、获取数组的元素

    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.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.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.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);

示例

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);

}

ArrayHandle.cpp

#include <jni.h>
#include <AndroidLog.h>
#include <string.h>

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;
}

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]);
        }
    }

在这里插入图片描述

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

2.1、获取数组下标元素

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

2.2、设置指定下标元素

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

2.3、获取数组长度

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

示例

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");
    }
}

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);

}

ArrayHandle.cpp

#include <jni.h>
#include <AndroidLog.h>
#include <string.h>
#include <stdlib.h>

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;
}

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());
        }
    }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值