Android NDK学习:JNI中的数组、引用和异常的处理

JNI的文档

https://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html

JNI数组操作

调用数组

Java 方法

    //给数组排序
    public native void sortArray(int[] array);

C 代码

//类似java比较器
int fCompare(const int* a, const int* b){
    return (*a) - (*b);
}


JNIEXPORT void JNICALL Java_com_liu_JniUtils_sortArray
(JNIEnv * env, jobject obj,jintArray arr){
    //转化数组
    jint* int_arr = (*env)->GetIntArrayElements(env, arr, NULL);
    //获取数组的长度
    int len = (*env)->GetArrayLength(env, arr);
    //排序。数组,数组的长度,单个元素的大小,比较器
    qsort(int_arr, len, sizeof(int), fCompare);


    //非常重要的同步。不然的话,数据没有变化
    (*env)->ReleaseIntArrayElements(env, arr, int_arr, 0);

}

ReleaseIntArrayElements()函数最后一个值:
- 0。Java数组更新,释放C/C++数组
- JNI_ABORT。Java数组不更新,释放C/C++数组
- JNI_COMMIT。Java数组更新,不释放C/C++数组。(函数执行完,数组也会释放)

返回一个数组

Java 代码

    public native int[] getDatas(int len);

C 代码

//返回一个数组
JNIEXPORT jintArray JNICALL Java_com_liu_JniUtils_getDatas
(JNIEnv * env, jobject obj,jint len){
    //创建一个指定大小的数组
    jintArray array = (*env)->NewIntArray(env, len);
    int* ints = (*env)->GetIntArrayElements(env, array, NULL);

    int i = 0;
    for (; i < len; i++){
        ints[i] = i;
    }
    //同步,不然,没有值
    (*env)->ReleaseIntArrayElements(env, array, ints, 0);
    return array;
}

引用的处理

引用类型有:局部引用和全局引用
作用:让虚拟机回收一个JNI变量

局部引用

当创建了大量的局部引用,在使用完成的时候,后面没有执行完,需要回收。

java 代码

public native void useLocalRef();

C 代码

//局部引用
JNIEXPORT jintArray JNICALL Java_com_liu_JniUtils_useLocalRef
(JNIEnv * env, jobject obj){
    //创建Date对象
    jclass cls = (*env)->FindClass(env,"java/util/Date");
    jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    jobject date = (*env)->NewObject(env, cls, mid);


    //使用完了。回收对象
    (*env)->DeleteLocalRef(env, date);
}

全局引用

它可以在多个函数共享。
然后,手动控制内存的释放。

    //生成string,java打印
    public native String getGlobalRef();
    //销毁string
    public native void useAndDelGlobalRef();

C 代码

jstring global_str;

//全局引用,获取
JNIEXPORT jstring JNICALL Java_com_liu_JniUtils_getGlobalRef
(JNIEnv * env, jobject obj){
    jstring str = (*env)->NewStringUTF(env, "this is a global str");
    global_str = (*env)->NewGlobalRef(env, str);
    return global_str;
}
//全局引用。销毁
JNIEXPORT void JNICALL Java_com_liu_JniUtils_useAndDelGlobalRef
(JNIEnv * env, jobject obj){


    (*env)->DeleteGlobalRef(env, global_str);
}

弱全局引用

对象不常用时,可以使用。内存不足时,可以释放该对象。

跟全局引用创建,销毁的方法类似

//创建
(*env)->NewWeakGlobalRef(env,str);
//销毁
(*env)->DeleteWeakGlobalRef(env,str)

异常处理


当c/c++代码出现异常后,它们还是会向下走的。所以,当检测到异常后,我们需要return;
防止下面的接着出现异常。

异常处理有:
- 自己处理
- 抛给Java层,Java捕获,处理

自己处理异常

(*env)->ExceptionOccurred,ExceptionCheck来判断是否发生了异常。

JNIEXPORT void JNICALL Java_com_liu_demojni2_MainActivity_stringFromJNI(
        JNIEnv *env,jobject ojb) {
    char* a = NULL;
    *a = "b";

    //自己处理异常,
    if((*env)->ExceptionCheck(env)){
        //打印信息
        (*env)->ExceptionDescribe(env);
        //清空异常
        (*env)->ExceptionClear(env);
        //补救或者return;
        return ;
    }
    //不往下执行

把异常抛给Java层

JNIEXPORT void JNICALL Java_com_liu_demojni2_MainActivity_stringFromJNI(
        JNIEnv *env,jobject ojb) {
    ....
    //把异常抛给Java层
    if (((*env)->ExceptionOccurred(env))){
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        jclass cls = (*env)->FindClass(env, "java/lang/Exception");
        (*env)->ThrowNew(env, cls, "this is a exception!");
        return;
    }
    ....
}

异常调试


在build文件里面有两个so的文件,cmake跟transform文件夹里面。
而且,cmake里面的会比transform里面的大很多。
cmake里面的就是带符号表的so。这个很重要,我们可以根据符号表来定位到,我们的具体错误位置。

通过’ndk-stack’ 简单工具,来定位Native异常的具体位置。

adb logcat | $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi
//或者从文件读取
 adb logcat > /tmp/foo.txt
    $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi -dump foo.txt

模拟一个异常

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

#include "com_liu_demojni2_MainActivity.h"

JNIEXPORT jstring JNICALL Java_com_liu_demojni2_MainActivity_stringFromJNI(
        JNIEnv *env,jobject ojb) {
    char* a = NULL;
    *a = "b";
    return (*env)->NewStringUTF(env,"from C");
}

通过命令

D:\develop\workspace\wort_studio\DemoJni2>adb logcat|ndk-stack -sym app\build\intermediates\cmake\debug\obj\x86

打印的信息

********** Crash dump: **********
Build fingerprint: 'google/sdk_gphone_x86/generic_x86:8.1.0/OSM2.171116.002/4458339:userdebug/dev-keys'
pid: 3991, tid: 3991, name: om.liu.demojni2  >>> com.liu.demojni2 <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Stack frame 04-11 23:37:15.017  4019  4019 F DEBUG   :     #00 pc 00000510  /data/app/com.liu.demojni2-8NcMhlAmcuqtiWQHojTaVg==/lib/x86/libnative-lib.so (Java_
com_liu_demojni2_MainActivity_stringFromJNI+64): Routine Java_com_liu_demojni2_MainActivity_stringFromJNI at D:\develop\workspace\wort_studio\DemoJni2\app\src\
main\cpp/native-lib.c:9
Stack frame 04-11 23:37:15.017  4019  4019 F DEBUG   :     #01 pc 00009158  /data/app/com.liu.demojni2-8NcMhlAmcuqtiWQHojTaVg==/oat/x86/base.odex (offset 0x900
0)


这里就看到cpp/native-lib.c:9 在这个c文件的第九行就打印出来了。

猜你喜欢

转载自blog.csdn.net/ecliujianbo/article/details/79912126