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