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文件的第九行就打印出来了。