JNI語法小結(jié)

JNI是什么?

JNI的全稱就是Java Native Interface,顧名思義,就是Java和C/C++相互通信的接口,就好比買賣房子都需要找中介一樣,這里的JNI就是Java和C/C++通信的中介,一個中間人。

JNI頭文件

JNI開發(fā)前提是要引入jni.h頭文件,這個文件Android NDK目錄下面。示例如下:

include<jni.h>

怎么加載so庫?Android提供了3個實用的函數(shù)用來加載JNI庫,分別是

System.loadLibrary(libname)
Runtime.getRuntime().loadLibrary(libname)
Runtime.getRuntime().load(libFilePath)

用loadLibrary函數(shù)加載

用System.loadLibrary(libname)和Runtime.getRuntime().loadLibrary(libname)這兩個函數(shù)加載so庫,不需要指定so庫的路徑,Android會默認(rèn)從系統(tǒng)的共享庫目錄里面去查找,Android的共享庫目錄就是vendor/lib和system/lib,如果在共享庫路徑里面找到指定名字的so庫,就會立即加載這個so庫,所以我們給so庫起名的時候要盡量避免和Android共享庫里面的so庫同名。如果在共享庫目錄里面查找不到,就會在APP的安裝目錄里面查找APP的私有so庫,如果查找到,會立即加載這個so庫。

用load函數(shù)加載

Runtime.getRuntime().load(libFilePath)用這個函數(shù)加載so庫,需要指定完整的so庫路徑,優(yōu)點(diǎn)是加載速度快,并且不會加載錯誤的so庫,缺點(diǎn)就是需要指定完整的so庫路徑,有時候并不方便,大家常用的加載so庫的方式還是用loadLibrary函數(shù)來加載。

加載so庫示例

static {
    System.loadLibrary("native-lib");
    //用這種方式加載so庫和System.loadLibrary函數(shù)加載so庫的效果是一樣的
    //Runtime.getRuntime().loadLibrary("native-lib");
    //String soLibFilePath;
    //用這種方式加載so庫需要指定完整的so庫路徑
    //Runtime.getRuntime().load(soLibFilePath);
}
Android Studio so庫配置

Android Studio通過CMakeLists.txt文件配置需要生成的so庫,下面詳細(xì)介紹一下這個CMakeLists.txt文件如何配置。

Android Studio通過cmake命令來生成so庫。

CMakeLists.txt文件配置詳解

add_library

add_library函數(shù)用來配置要生成的so庫的基本信息,比如庫的名字,要生成的so庫是靜態(tài)的還是共享的,so庫的C/C++源文件列表

示例如下:

add_library( native-lib
             SHARED
             src/main/cpp/native-lib.cpp
             src/main/cpp/native-lib2.cpp
             src/main/cpp/native-lib3.cpp)

第一個參數(shù)是so庫的名字

第二個參數(shù)是要生成的so庫的類型,靜態(tài)so庫是STATIC,共享so庫是SHARED

第三個參數(shù)是C/C++源文件,可以包括多個源文件

find_library

find_library函數(shù)用來從NDK目錄下面查找特定的so庫。示例如下:

find_library( log-lib
              log )

第一個參數(shù)是我們給要查找的so庫起的名字,名字可以隨便寫

第二個參數(shù)是要查找的so庫的名字,這個名字是從真實的so庫的名字去掉前綴和后綴后的名字,比如liblog.so這個so庫的名字就是log

target_link_libraries

target_link_libraries函數(shù)用來把要生成的so庫和依賴的其它so庫進(jìn)行鏈接,生成我們需要的so庫文件。示例如下:

target_link_libraries( native-lib
                       ${log-lib} )

第一個參數(shù)是我們要生成的so庫的名字去掉前綴和后綴后的名字,在這個例子中,要生成的真實的so庫的名字是libnative-lib.so

第二個參數(shù)是鏈接我們用find_library函數(shù)定義的查找的依賴庫的名字log-lib,語法就是${依賴的庫的名字}

Java和JNI類型對照表

這里詳細(xì)介紹一下Java類型和C/C++類型的對照關(guān)系,方便我們下面的學(xué)習(xí),這一部分知識很基礎(chǔ),也很重要。

Java和JNI基本類型對照表

Java的基本類型可以直接與C/C++的基本類型映射,因此Java的基本類型對開發(fā)人員是透明的。

Java類型 本地類型 描述
boolean jboolean C/C++8位整型
byte jbyte C/C++帶符號的8位整型
char jchar C/C++無符號的16位整型
short jshort C/C++帶符號的16位整型
int jint C/C++帶符號的32位整型
long jlong C/C++帶符號的64位整型e
float jfloat C/C++32位浮點(diǎn)型
double jdouble C/C++64位浮點(diǎn)型
Class jclass Class對象
String jstring 字符串對象
Object[] jobjectArray 任何對象的數(shù)組
boolean[] jbooleanArray 布爾型數(shù)組
byte[] jbyteArray 比特型數(shù)組
char[] jcharArray 字符型數(shù)組
short[] jshortArray 短整型數(shù)組
int[] jintArray 整型數(shù)組
long[] jlongArray 長整型數(shù)組
float[] jfloatArray 浮點(diǎn)型數(shù)組
double[] jdoubleArray 雙浮點(diǎn)型數(shù)組
Object jobject 任何Java對象,或者沒有對應(yīng)java類型的對象
JNI字符串相關(guān)的函數(shù)

C/C++字符串轉(zhuǎn)JNI字符串

NewString函數(shù)用來生成Unicode JNI字符串

NewStringUTF函數(shù)用來生成UTF-8 JNI字符串

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
    char *str="helloboy";
    jstring jstr2=env->NewStringUTF(str);

    const jchar *jchar2=env->GetStringChars(jstr,NULL);
    size_t len=env->GetStringLength(jstr);
    jstring jstr3=env->NewString(jchar2,len);
}

JNI字符串轉(zhuǎn)C/C++字符串

GetStringChars函數(shù)用來從jstring獲取Unicode C/C++字符串

GetStringUTFChars函數(shù)用來從jstring獲取UTF-8 C/C++字符串

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
    const char *str=env->GetStringUTFChars(jstr,NULL);
    const jchar *jchar2=env->GetStringChars(jstr,NULL);
}

釋放JNI字符串

ReleaseStringChars函數(shù)用來釋放Unicode C/C++字符串

ReleaseStringUTFChars函數(shù)用來釋放UTF-8 C/C++字符串

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
    const char *str=env->GetStringUTFChars(jstr,NULL);
   env->ReleaseStringUTFChars(jstr,str);
    
    const jchar *jchar2=env->GetStringChars(jstr,NULL);
   env->ReleaseStringChars(jstr,jchar2);
}

JNI字符串截取

GetStringRegion函數(shù)用來截取Unicode JNI字符串

GetStringUTFRegion函數(shù)用來截取UTF-8 JNI字符串

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
    const char *str=env->GetStringUTFChars(jstr,NULL);
    char *subStr=new char;
    env->GetStringUTFRegion(jstr,0,3,subStr);
   env->ReleaseStringUTFChars(jstr,str);

    const jchar *jchar2=env->GetStringChars(jstr,NULL);
    jchar *subJstr=new jchar;
    env->GetStringRegion(jstr,0,3,subJstr);
   env->ReleaseStringChars(jstr,jchar2);
}

獲取JNI字符串的長度

GetStringLength用來獲取Unicode JNI字符串的長度

GetStringUTFLength函數(shù)用來獲取UTF-8 JNI字符串的長度

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
    jsize len=env->GetStringLength(jstr);
    jsize len2=env->GetStringUTFLength(jstr);
}

獲取JNI基本類型數(shù)組元素

Get<Type>ArrayElements函數(shù)用來獲取基本類型JNI數(shù)組的元素,這里面的<Type>需要被替換成實際的類型,比如GetIntArrayElements,GetLongArrayElements等

示例代碼如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
    jint *intArray=env->GetIntArrayElements(array,NULL);
    int len=env->GetArrayLength(array);
    for(int i=0;i<len;i++){
        jint item=intArray[i];
    }
}

獲取JNI基本類型數(shù)組的子數(shù)組

Get<Type>ArrayRegion函數(shù)用來獲取JNI數(shù)組的子數(shù)組,這里面的<Type>需要被替換成實際的類型,比如GetIntArrayRegion,GetLongArrayRegion等

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
    jint *subArray=new jint;
    env->GetIntArrayRegion(array,0,3,subArray);
}

設(shè)置JNI基本類型數(shù)組的子數(shù)組

Set<Type>ArrayRegion函數(shù)用來獲取JNI基本類型數(shù)組的子數(shù)組,這里面的<Type>需要被替換成實際的類型,比如SetIntArrayRegion,SetLongArrayRegion等

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
    jint *subArray=new jint;
    env->GetIntArrayRegion(array,0,3,subArray);
    env->SetIntArrayRegion(array,0,3,subArray);
}

JNI對象數(shù)組

GetObjectArrayElement函數(shù)用來獲取JNI對象數(shù)組元素

SetObjectArrayElement函數(shù)用來設(shè)置JNI對象數(shù)組元素

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJObjectArray(JNIEnv* env, jobject thiz,jobjectArray array) {
    int len=env->GetArrayLength(array);
    for(int i=0;i<len;i++)
    {
        jobject item=env->GetObjectArrayElement(array,i);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJStringArray(JNIEnv* env, jobject thiz,jobjectArray array) {
    int len=env->GetArrayLength(array);
    for(int i=0;i<len;i++)
    {
        jstring item=(jstring)env->GetObjectArrayElement(array,i);
    }
}

 

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJObjectArray(JNIEnv* env, jobject thiz,jobjectArray array) {
    jobject obj;
    env->SetObjectArrayElement(array,1,obj);
}

獲取JNI數(shù)組的長度

GetArrayLength用來獲取數(shù)組的長度

示例如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJObjectArray(JNIEnv* env, jobject thiz,jobjectArray array) {
    int len=env->GetArrayLength(array);
}
 
extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
    int len=env->GetArrayLength(array);
}

JNI NIO緩沖區(qū)相關(guān)的函數(shù)

使用NIO緩沖區(qū)可以在Java和JNI代碼中共享大數(shù)據(jù),性能比傳遞數(shù)組要快很多,當(dāng)Java和JNI需要傳遞大數(shù)據(jù)時,推薦使用NIO緩沖區(qū)的方式來傳遞。

NewDirectByteBuffer函數(shù)用來創(chuàng)建NIO緩沖區(qū)

GetDirectBufferAddress函數(shù)用來獲取NIO緩沖區(qū)的內(nèi)容

GetDirectBufferCapacity函數(shù)用來獲取NIO緩沖區(qū)的大小

示例代碼如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testDirectBuffer(JNIEnv* env, jobject thiz) {
    const char *data="hello world";
    int len=strlen(data);
    jobject obj=env->NewDirectByteBuffer((void*)data,len);
    long capicity=env->GetDirectBufferCapacity(obj);
    char *data2=(char*)env->GetDirectBufferAddress(obj);
}

JNI訪問Java類的方法和字段

Java類型簽名映射表

JNI獲取Java類的方法ID和字段ID,都需要一個很重要的參數(shù),就是Java類的方法和字段的簽名,這個簽名需要通過下面的表來獲取。

Java類型 簽名
Boolean Z
Byte B
Char C
Short S
Integer I
Long J
Float F
Double D
Void V
任何Java類的全名 L任何Java類的全名;比如Java String類對應(yīng)的簽名是Ljava/lang/String;
type[] type[這個就是Java數(shù)組的簽名,比如Java int[]的簽名是[I,Java long[]的簽名就是[J,Java String[]的簽名是 [Ljava/lang/String;
方法類型 (參數(shù)類型)返回值 類型,比如Java方法void hello(String msg,String msg2)對應(yīng)的簽名就是(Ljava/lang/String;Ljava/lang/String;)V 再比如Java方法String getNewName(String name)對應(yīng)的簽名是(Ljava/lang/String;) Ljava/lang/String;

再比如Java方法long add(int a,int b)對應(yīng)的簽名是(II)J

JNI訪問Java類方法相關(guān)的函數(shù)

JNI訪問Java類的實例方法

GetObjectClass函數(shù)用來獲取Java對象對應(yīng)的類類型

GetMethodID函數(shù)用來獲取Java類實例方法的方法ID

Call<Type>Method函數(shù)用來調(diào)用Java類實例特定返回值的方法,比如CallVoidMethod,調(diào)用java沒有返回值的方法,CallLongMethod用來調(diào)用Java返回值為Long的方法,等等。

示例如下:

Java代碼:

public native void callJavaHelloWorld2();
 
public void helloWorld2(String msg){
    Log.i("hello","hello world "+msg);
}
 
JNI代碼:
extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld2(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld2_methodID=env->GetMethodID(clazz,"helloWorld2","(Ljava/lang/String;)V");
    if(helloWorld2_methodID==NULL) return;
    const char *msg="hello world";
    jstring jmsg=env->NewStringUTF(msg);
    env->CallVoidMethod(thiz,helloWorld2_methodID,jmsg);
}

JNI訪問Java類的靜態(tài)方法

GetObjectClass函數(shù)用來獲取Java對象對應(yīng)的類類型

GetStaticMethodID函數(shù)用來獲取Java類靜態(tài)方法的方法ID

CallStatic<Type>Method函數(shù)用來調(diào)用Java類特定返回值的靜態(tài)方法,比如CallStaticVoidMethod,調(diào)用java沒有返回值的靜態(tài)方法,CallStaticLongMethod用來調(diào)用Java返回值為Long的靜態(tài)方法,等等。

示例如下:

Java代碼:

public native void callStaticJavaHelloWorld2();
 
public static void helloWorldStatic2(String msg){
    Log.i("hello","hello world static "+msg);
}
JNI代碼:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld2(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorldStatic2_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic2","(java/lang/String;)V");
    if(helloWorldStatic2_methodID==NULL) return;
    const char *msg="hello world";
    jstring jmsg=env->NewStringUTF(msg);
    env->CallStaticVoidMethod(clazz,helloWorldStatic2_methodID,msg);
}

JNI訪問Java類字段相關(guān)的函數(shù)

JNI訪問Java類實例字段

GetFieldID函數(shù)用來獲取Java字段的字段ID

Get<Type>Field用來獲取Java類字段的值,比如用GetIntField函數(shù)獲取Java int型字段的值,用GetLongField函數(shù)獲取Java long字段的值,用GetObjectField函數(shù)獲取Java引用類型字段的值

示例如下:

Java代碼:

public class Person{
    public String name;
    public int age;
}

public native void getJavaObjectField(Person person);
 
private void test(){
    Person person=new Person();
    person.name="wubb";
    person.age=20;
    getJavaObjectField(person);
}
 

JNI代碼:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_getJavaObjectField(JNIEnv* env, jobject thiz,jobject person) {
    jclass clazz=env->GetObjectClass(person);
    jfieldID name_fieldID=env->GetFieldID(clazz,"name","Ljava/lang/String;");
    jstring name=(jstring) env->GetObjectField(person,name_fieldID);

    jfieldID age_fieldID=env->GetFieldID(clazz,"age","I");
    jint age=env->GetIntField(person,age_fieldID);
}

JNI訪問Java類靜態(tài)字段

GetStaticFieldID函數(shù)用來獲取Java靜態(tài)字段的字段ID

GetStatic<Type>Field用來獲取Java類靜態(tài)字段的值,比如用GetStaticIntField函數(shù)獲取Java 靜態(tài)int型字段的值,用GetStaticLongField函數(shù)獲取Java 靜態(tài)long字段的值,用GetStaticObjectField函數(shù)獲取Java靜態(tài)引用類型字段的值

示例如下:

Java代碼:

public class Person {
    public String name;
    public int age;

    public static String name_static;
    public static int age_static;
}
 
public native void getJavaObjectStaticField(Person person);
 
private void test(){
    Person.name_static="wubb";
    Person.age_static=20;

    Person person=new Person();
    getJavaObjectStaticField(person);
}
 
JNI代碼:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_getJavaObjectStaticField(JNIEnv* env, jobject thiz,jobject person) {
    jclass clazz=env->GetObjectClass(person);
    jfieldID name_fieldID=env->GetStaticFieldID(clazz,"name_static","Ljava/lang/String;");
    jstring name=(jstring) env->GetStaticObjectField(clazz,name_fieldID);

    jfieldID age_fieldID=env->GetStaticFieldID(clazz,"age_static","I");
    jint age=env->GetStaticIntField(clazz,age_fieldID);
}

JNI線程同步相關(guān)的函數(shù)

JNI可以使用Java對象進(jìn)行線程同步

MonitorEnter函數(shù)用來鎖定Java對象

MonitorExit函數(shù)用來釋放Java對象鎖

示例如下:

Java代碼:

jniLock(new Object());
 
JNI代碼:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_jniLock(JNIEnv* env, jobject thiz,jobject obj) {
    env->MonitorEnter(obj);
    //do something
    env->MonitorExit(obj);
}

JNI異常相關(guān)的函數(shù)

JNI處理Java異常

當(dāng)JNI函數(shù)調(diào)用的Java方法出現(xiàn)異常的時候,并不會影響JNI方法的執(zhí)行,但是我們并不推薦JNI函數(shù)忽略Java方法出現(xiàn)的異常繼續(xù)執(zhí)行,這樣可能會帶來更多的問題。我們推薦的方法是,當(dāng)JNI函數(shù)調(diào)用的Java方法出現(xiàn)異常的時候,JNI函數(shù)應(yīng)該合理的停止執(zhí)行代碼。

ExceptionOccurred函數(shù)用來判斷JNI函數(shù)調(diào)用的Java方法是否出現(xiàn)異常

ExceptionClear函數(shù)用來清除JNI函數(shù)調(diào)用的Java方法出現(xiàn)的異常

請看如下示例:

Java代碼

public void helloWorld(){
    throw new NullPointerException("null pointer occurred");
    //Log.i("hello","hello world");
}
C++代碼

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld_methodID=env->GetMethodID(clazz,"helloWorld","()V");
    if(helloWorld_methodID==NULL) return;
    env->CallVoidMethod(thiz,helloWorld_methodID);
    if(env->ExceptionOccurred()!=NULL){
        env->ExceptionClear();
        __android_log_print(ANDROID_LOG_VERBOSE,"hello","%s","program end with java exception");
        return;
    }
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","%s","program end normallly");
}

JNI拋出Java類型的異常

JNI通過ThrowNew函數(shù)拋出Java類型的異常

示例如下:

Java代碼

try
{
    testNativeException();
}
catch (NullPointerException e){
    e.printStackTrace();
}
C++代碼

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testNativeException(JNIEnv* env, jobject thiz) {
    jclass clazz=env->FindClass("java/lang/NullPointerException");
    if(clazz==NULL) return;
    env->ThrowNew(clazz,"null pointer exception occurred");
}

JNI對象的全局引用和局部引用

我們知道Java代碼的內(nèi)存是由垃圾回收器來管理,而JNI代碼則不受Java的垃圾回收器來管理,所以JNI代碼提供了一組函數(shù),來管理通過JNI代碼生成的JNI對象,比如jobject,jclass,jstring,jarray等,對于這些對象,我們不能簡單的在JNI代碼里面聲明一個全局變量,然后把JNI對象賦值給全局變量,我們需要采用JNI代碼提供的專有函數(shù)來管理這些全局的JNI對象。

JNI對象的局部引用

在JNI接口函數(shù)中引用JNI對象的局部變量,都是對JNI對象的局部引用,一旦JNI接口函數(shù)返回,所有這些JNI對象都會被自動釋放。不過我們也可以采用JNI代碼提供的DeleteLocalRef函數(shù)來刪除一個局部JNI對象引用。

示例代碼:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testDeleteLocalRef(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld_methodID=env->GetMethodID(clazz,"helloWorld","()V");
    if(helloWorld_methodID==NULL) return;
   env->CallVoidMethod(thiz,helloWorld_methodID);
    env->DeleteLocalRef(clazz);
}

JNI對象的全局引用

對于JNI對象,絕對不能簡單的聲明一個全局變量,在JNI接口函數(shù)里面給這個全局變量賦值這么簡單,一定要使用JNI代碼提供的管理JNI對象的函數(shù),否則代碼可能會出現(xiàn)預(yù)想不到的問題。JNI對象的全局引用分為兩種,一種是強(qiáng)全局引用,這種引用會阻止Java的垃圾回收器回收J(rèn)NI代碼引用的Java對象,另一種是弱全局引用,這種全局引用則不會阻止垃圾回收器回收J(rèn)NI代碼引用的Java對象。

強(qiáng)全局引用

NewGlobalRef用來創(chuàng)建強(qiáng)全局引用的JNI對象

DeleteGlobalRef用來刪除強(qiáng)全局引用的JNI對象

示例如下:

jobject gThiz;
extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testStrongGlobalRef(JNIEnv* env, jobject thiz) {
    //gThiz=thiz;//不能這樣給全局JNI對象賦值,要采用下面這種方式
    gThiz=env->NewGlobalRef(thiz);//生成全局的JNI對象引用,這樣生成的全局的JNI對象才可以在其它函數(shù)中使用

    env->DeleteGlobalRef(gThiz);//假如我們不需要gThiz這個全局的JNI對象引用,我們可以把它刪除掉
}

弱全局引用

NewWeakGlobalRef用來創(chuàng)建弱全局引用的JNI對象

DeleteWeakGlobalRef用來刪除弱全局引用的JNI對象

IsSameObject用來判斷兩個JNI對象是否相同

示例如下:

jobject gThiz;
extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_testWeakGlobalRef(JNIEnv*env, jobject thiz) {
    //gThiz=thiz;//不能這樣給全局JNI對象賦值,要采用下面這種方式
    gThiz=env->NewWeakGlobalRef(thiz);//生成全局的JNI對象引用,這樣生成的全局的JNI對象才可以在其它函數(shù)中使用

    if(env->IsSameObject(gThiz,NULL)){
        //弱全局引用已經(jīng)被Java的垃圾回收器回收
    }

    env->DeleteWeakGlobalRef(gThiz);//假如我們不需要gThiz這個全局的JNI對象引用,我們可以把它刪除掉
}

Java代碼和JNI代碼通信

Java通過JNI接口調(diào)用C/C++方法
首先我們需要在Java代碼里面聲明Native方法原型,比如:

public native void helloJNI(String msg);
 
其次我們需要在C/C++代碼里面聲明JNI方法原型,比如:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv* env, jobject thiz,jstring msg) {
    //do something
}
現(xiàn)在這段JNI函數(shù)聲明代碼采用的是C++語言寫的,所以需要添加extern "C"聲明,如果源代碼是C語言,則不需要添加這個聲明。

JNIEXPORT 這個關(guān)鍵字說明這個函數(shù)是一個可導(dǎo)出函數(shù),學(xué)過C/C++的朋友都知道,C/C++ 庫里面的函數(shù)有些可以直接被外部調(diào)用,有些不可以,原因就是每一個C/C++庫都有一個導(dǎo)出函數(shù)列表,只有在這個列表里面的函數(shù)才可以被外部直接調(diào)用,類似Java的public函數(shù)和private函數(shù)的區(qū)別。

JNICALL 說明這個函數(shù)是一個JNI函數(shù),用來和普通的C/C++函數(shù)進(jìn)行區(qū)別,實際發(fā)現(xiàn)不加這個關(guān)鍵字,Java也是可以調(diào)用這個JNI函數(shù)的。

Void 說明這個函數(shù)的返回值是void,如果需要返回值,則把這個關(guān)鍵字替換成要返回的類型即可。

Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv*env, jobject thiz,jstring msg)這是完整的JNI函數(shù)聲明,JNI函數(shù)名的原型如下:

Java_ + JNI方法所在的完整的類名,把類名里面的”.”替換成”_” + 真實的JNI方法名,這個方法名要和Java代碼里面聲明的JNI方法名一樣+ JNI函數(shù)必須的默認(rèn)參數(shù)(JNIEnv* env, jobjectthiz)

env參數(shù)是一個指向JNIEnv函數(shù)表的指針,

thiz參數(shù)代表的就是聲明這個JNI方法的Java類的引用

msg參數(shù)就是和Java聲明的JNI函數(shù)的msg參數(shù)對于的JNI函數(shù)參數(shù)

JNI函數(shù)的原型

[extern “C”]JNIEXPORT 函數(shù)返回值 JNICALL 完整的函數(shù)聲明(JNIENV *env, jobject thiz, …)

其中extern “C”根據(jù)需要動態(tài)添加,如果是C++代碼,則必須要添加extern “C”聲明,如果是C代碼,則不用添加

靜態(tài)JNI方法和實例JNI方法區(qū)別

先看一個示例:

Java代碼:

public native void showHello();
public native static void showHello2();
C++代碼:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_showHello(JNIEnv* env, jobject thiz) {
    //do something
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_showHello2(JNIEnv* env, jclass thiz) {
    //do something
}

相信明眼的同學(xué)很快就能發(fā)現(xiàn)這兩個JNI函數(shù)的區(qū)別,對就是這個區(qū)別,普通的JNI方法對應(yīng)的JNI函數(shù)的第二個參數(shù)是jobject類型,而靜態(tài)的JNI方法對應(yīng)的JNI函數(shù)的第二個參數(shù)是jclass類型

常見的Java JNI方法聲明和JNI函數(shù)聲明示例

Java Native方法聲明:

public class Person{
    public String name;
    public int age;
}

public native void helloJNI(String msg);
public native int func1(int a,int b);
public native String func2(String str);
public native void func3(boolean b);
public native void func4(Person person);
public native static void func5();
 
C++JNI函數(shù)聲明:

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv* env, jobject thiz,jstring msg) {
    //do something
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_func1(JNIEnv* env, jobject thiz,jint a,jint b) {
    //do something
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_func2(JNIEnv* env, jobject thiz,jstring str) {
    //do something
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_func3(JNIEnv* env, jobject thiz,jboolean b) {
    //do something
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_func4(JNIEnv* env, jobject thiz,jobject person) {
    //do something
}
 
extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_func5(JNIEnv* env, jclass thiz) {
    //do something
}
 
所有的Java類對象在JNI函數(shù)里面都使用jobject來表示

JNI代碼和Java代碼通信

C++調(diào)用Java實例方法示例

Java代碼

public native void callJavaHelloWorld();
public native void callJavaHelloWorld2();
public native void callJavaHelloWorld3();
 

public void helloWorld(){
    Log.i("hello","helloworld");
}

public void helloWorld2(String msg){
    Log.i("hello","helloworld "+msg);
}

public void helloWorld3(inta,int b){
    int c=a+b;
    Log.i("hello","helloworld c="+c);
}

C++代碼

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld_methodID=env->GetMethodID(clazz,"helloWorld","()V");
    if(helloWorld_methodID==NULL) return;
    env->CallVoidMethod(thiz,helloWorld_methodID);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld2(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld2_methodID=env->GetMethodID(clazz,"helloWorld2","(java/lang/String;)V");
    if(helloWorld2_methodID==NULL) return;
    const char *msg="hello world";
    jstring jmsg=env->NewStringUTF(msg);
    env->CallVoidMethod(thiz,helloWorld2_methodID,jmsg);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld3(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorld3_methodID=env->GetMethodID(clazz,"helloWorld3","(II)V");
    if(helloWorld3_methodID==NULL) return;
    env->CallVoidMethod(clazz,helloWorld3_methodID,2,3);
}
C++調(diào)用Java靜態(tài)方法示例

Java代碼

public native void callStaticJavaHelloWorld();
public native void callStaticJavaHelloWorld2();
public native void callStaticJavaHelloWorld3();

public static void helloWorldStatic(){
    Log.i("hello","helloworld static");
}

public static void helloWorldStatic2(String msg){
    Log.i("hello","helloworld static "+msg);
}

public static void helloWorldStatic3(inta,int b){
    int c=a+b;
    Log.i("hello","helloworld static c="+c);
}

C++代碼

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorldStatic_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic","()V");
    if(helloWorldStatic_methodID==NULL) return;
    env->CallStaticVoidMethod(clazz,helloWorldStatic_methodID);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld2(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorldStatic2_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic2","(java/lang/String;)V");
    if(helloWorldStatic2_methodID==NULL) return;
    const char *msg="hello world";
    jstring jmsg=env->NewStringUTF(msg);
    env->CallStaticVoidMethod(clazz,helloWorldStatic2_methodID,msg);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld3(JNIEnv* env, jobject thiz) {
    jclass clazz=env->GetObjectClass(thiz);
    if(clazz==NULL) return;
    jmethodID helloWorldStatic3_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic3","(II)V");
    if(helloWorldStatic3_methodID==NULL) return;
    env->CallStaticVoidMethod(clazz,helloWorldStatic3_methodID,2,3);
}

下載地址:Github

最后編輯于
?著作權(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)容