Android NDK學習:JNI中的陣列、引用和異常的處理
阿新 • • 發佈:2019-01-24
JNI的文件
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檔案的第九行就打印出來了。