JNI基礎(chǔ)(3)

基本數(shù)據(jù)類型

Java Type Native Type
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble

引用數(shù)據(jù)類型

Java Type Native Type
Object jobject
Class jclass
String jstring
Object[] jobjectArray
boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray
void void

JNI基本描述符

Java Type Filed Descriptor
boolean Z
byte B
char C
short S
int I
long J
float F
bouble D

JNI引用類型描述符

一般引用類型描述符的規(guī)則如下,注意不要丟掉“;”

L + 類描述符 + ;

如,String 類型的域描述符為:

Ljava/lang/String;

數(shù)組的域描述符特殊一點(diǎn),如下,其中有多少級數(shù)組就有多少個“[”,數(shù)組的類型為類時,則有分號,為基本類型時沒有分號

[ + 其類型的域描述符

例如:

int[]      描述符為 [I
double[]   描述符為 [D
String[]   描述符為 [Ljava/lang/String;
Object[]   描述符為 [Ljava/lang/Object;
int[][]    描述符為 [[I
double[][] 描述符為 [[D

JNI函數(shù)簽名

函數(shù)簽名需要將所有參數(shù)類型的域描述符按照聲明順序放入括號,然后再加上返回值類型的域描述符,其中沒有參數(shù)時,不需要括號,如下規(guī)則:

(參數(shù)……)返回類型

例如:

String getString()  ——>  Ljava/lang/String;
int sum(int a, int b)  ——>  (II)I
void main(String[] args) ——> ([Ljava/lang/String;)V

JNI內(nèi)存管理

在c++中new的對象,如果不返回java,必須用release掉,否則內(nèi)存泄露。包括NewStringUTF,NewObject。如果返回java不必release,java會自己回收。

1.FindClass

jclass ref= (env)->FindClass("java/lang/String");

env->DeleteLocalRef(ref); 

2.NewString / NewStringUTF / NewObject / NewByteArray

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

const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);

jstring     (*NewStringUTF)(JNIEnv*, const char*);   

const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);

 
env->DeleteLocalRef(ref); 

3.GetObjectField/GetObjectClass/GetObjectArrayElement

jclass ref = env->GetObjectClass(robj);

env->DeleteLocalRef(ref);

4.GetByteArrayElements和GetStringUTFChars

jbyte* array= (*env)->GetByteArrayElements(env,jarray,&isCopy);
(*env)->ReleaseByteArrayElements(env,jarray,array,0);

const char* input =(*env)->GetStringUTFChars(env,jinput, &isCopy);
(*env)->ReleaseStringUTFChars(env,jinput,input);

5.NewGlobalRef/DeleteGlobalRef

jobject     (*NewGlobalRef)(JNIEnv*, jobject);
void        (*DeleteGlobalRef)(JNIEnv*, jobject);

例如,

jobject ref= env->NewGlobalRef(customObj);
env->DeleteGlobalRef(customObj);

6.JVM:AttachCurrentThread/ DetachCurrentThread

#include <jni.h>

#include <stdio.h>

#include <malloc.h>

#include "logUtils.h"

static JavaVM  *jvm=NULL;
static jobject  jobj_callback=NULL;
static jmethodID  mid=NULL;
static int flag=-1;
static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

//在加載庫時執(zhí)行;
 jint JNI_OnLoad(JavaVM *vm,void *reserved){
    jvm=vm;
    LOGE("------- JNI_OnLoad -------");
    return JNI_VERSION_1_6;
}
//在卸載庫時執(zhí)行
 void JNI_OnUnload(JavaVM *vm,void *reserved){
    jvm=NULL;
    LOGE("------- JNI_OnUnload -------");
}
//pthread中執(zhí)行的函數(shù)
static void *nativeWork(void*args){
    LOGE(" ==================== pre nativeWork ==================== ");
    JNIEnv *env=NULL;
    //附加到當(dāng)前線程從JVM中取出JNIEnv, C/C++從子線程中直接回到Java里的方法時  必須經(jīng)過這個步驟
    if( (*jvm)->AttachCurrentThread(jvm,&env,NULL)==0 ){
        while (flag==0){
            if( jobj_callback==NULL ){
                //進(jìn)入等待
                pthread_cond_wait(&cond,&mutex);
            } else{
                //回調(diào)java中的線程
                (*env)->CallVoidMethod(env,jobj_callback,mid);
                (*env)->DeleteGlobalRef(env,jobj_callback);
                jobj_callback=NULL;
            }
        }
        //完畢-脫離當(dāng)前線程
        (*jvm)->DetachCurrentThread(jvm);
    }
    LOGE(" ==================== end nativeWork ==================== ");
    return (void*)1;
}

JNI異常處理

java代碼調(diào)用native方法,間接調(diào)用exceptionCallback()方法,引發(fā)除零異常

public static native void doit();

public static void exceptionCallback() {
      int a = 20 / 0;
      System.out.println("--->" + a);
}

 public static void normalCallback() {
      System.out.println("In Java: invoke normalCallback.");
 }
 

c++代碼:

void jni_doit(JNIEnv *env, jclass cls) {
    jthrowable exc = NULL;
    jmethodID mid = (env)->GetStaticMethodID(cls,"exceptionCallback","()V");
    if (mid != NULL) {
        (env)->CallStaticVoidMethod(cls,mid);
    }
   
    // 檢查JNI調(diào)用是否有引發(fā)異常
    if ((env)->ExceptionCheck()) {
        (env)->ExceptionDescribe();
        // 清除引發(fā)的異常,在Java層不會打印異常的堆棧信息
        (env)->ExceptionClear();
        (env)->ThrowNew((env)->FindClass("java/lang/Exception"),"JNI拋出的異常!");
    }
    
    mid = (env)->GetStaticMethodID(cls,"normalCallback","()V");
    if (mid != NULL) {
        (env)->CallStaticVoidMethod(cls,mid);
    }
}

在上面的例子中,我們調(diào)用了JNI的ExceptionCheck函數(shù)檢查最近一次JNi調(diào)用是否發(fā)生了異常,如果有異常這個函數(shù)返回JNI_TRUE,否則返回JNI_FALSE。當(dāng)檢測到異常時,我們調(diào)用ExceptionDescribe函數(shù)打印這個異常的堆棧信息,然后再調(diào)用ExceptionClear函數(shù)清除異常堆棧信息的緩沖區(qū)(如果不清除,后面調(diào)用ThrowNew拋出的異常堆棧信息會覆蓋前面的異常信息),最后調(diào)用ThrowNew函數(shù)手動拋出一個java.lang.Exception異常。但在JNI中拋出未捕獲的異常與Java的異常處理機(jī)制不一樣,在JNI中并不會立即終止本地方法的執(zhí)行,而是繼續(xù)執(zhí)行后面的代碼。這種情況需要我們手動來處理。如果不用return馬上退出方法的話,ThrowNew后面的代碼依然會繼續(xù)執(zhí)行,如程序運(yùn)行的結(jié)果一樣,仍然會回調(diào)normalCallback方法,打印出:invoke normalCallback.

??,謝謝大家的閱讀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容