日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jni java_JNI 常见用法

發布時間:2025/3/21 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jni java_JNI 常见用法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、Java 代碼 和JNI代碼通信

Java代碼通過JNI接口 調用 C/C++方法

1、首先我們需要在Java代碼中聲明Natvie方法原型

public native void helloJNI(String msg);

2、其次我們需要在C/C++代碼里聲明JNI方法的原型

如:

extern "C"

JNIEXPORT void JNICALL

Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv* env, jobject thiz,jstring msg) {

//do something

}

extern "C"。JNI函數聲明聲明代碼是用C++語言寫的,所以需要添加extern "C"聲明;如果源代碼是C語言聲明,則不需要添加這個聲明

JNIEXPORT。這個關鍵字表明這個函數是一個可導出函數。每一個C/C++庫都有一個導出函數列表,只有在這個列表里面的函數才可以被外部直接調用,類似Java的public函數和private函數的區別。

JNICALL。說明這個函數是一個JNI函數,用來和普通的C/C++函數進行區別。

Void 返回值類型

JNI函數名原型:Java_ + JNI方法所在的完整的類名,把類名里面的”.”替換成”_” + 真實的JNI方法名,這個方法名要和Java代碼里面聲明的JNI方法名一樣。

env 參數 是一個執行JNIENV函數表的指針。

thiz 參數 代表的是聲明這個JNI方法的Java類的引用。

msg 參數就是和Java聲明的JNI函數的msg參數對于的JNI函數參數

靜態JNI方法 和實例JNI方法的區別

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

}

二、java 和JNI類型對照表

Java 和JNI基本類型對照表

java的基本類型可以直接與C/C++的基本類型映射。

image

Java與JNI引用類型對照表

與Java基本類型不同,引用類型對開發人員是不透明的。Java內部數據結構并不直接向原生代碼開放。也就是說 C/C++代碼并不能直接訪問Java代碼的字段和方法

image

三、JNI 基本操作舉例

1、JNI操作 字符串

java 類 TestNatvie.java

/**

* 字符串相關測試代碼

* @param str

*/

public native void testJstring(String str);

C++文件 natvie-lib.cpp

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testJstring(JNIEnv *env, jobject instance,

jstring str_) {

//(1)生成JNI String

char const * str = "hello world!";

jstring jstring = env->NewStringUTF(str);

// (2) jstring 轉換成 const char * charstr

const char *charstr = env->GetStringUTFChars(str_, 0);

// (3) 釋放 const char *

env->ReleaseStringUTFChars(str_, charstr);

//(4) 獲取字符串子集

char * subStr = new char;

env->GetStringUTFRegion(str_,0,3,subStr);//截取字符串char*;

env->ReleaseStringUTFChars(str_, subStr);

}

2、JNI操作數組

java 類 TestNatvie.java

/**

* 整形數組相關代碼

* @param array

*/

public native void testIntArray(int []array);

/**

*

* Object Array 相關測試 代碼

* @param strArr

*/

public native void testObjectArray(String[]strArr);

C++文件 natvie-lib.cpp

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testIntArray(JNIEnv *env, jobject instance,

jintArray array_) {

//----獲取數組元素

//(1)獲取數組中元素

jint * intArray = env->GetIntArrayElements(array_,NULL);

int len = env->GetArrayLength(array_);//(2)獲取數組長度

LOGD("feifei len:%d",len);

for(int i = 0; i < len;i++){

jint item = intArray[i];

LOGD("feifei item[%d]:%d",i,item);

}

env->ReleaseIntArrayElements(array_, intArray, 0);

//----- 獲取子數組

jint *subArray = new jint;

env->GetIntArrayRegion(array_,0,3,subArray);

for(int i = 0;i<3;i++){

subArray[i]= subArray[i]+5;

LOGD("feifei subArray:[%d]:",subArray[i]);

}

//用子數組修改原數組元素

env->SetIntArrayRegion(array_,0,3,subArray);

env->ReleaseIntArrayElements(array_,subArray,0);//釋放子數組元素

}

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testObjectArray(JNIEnv *env, jobject instance,

jobjectArray strArr) {

//獲取數組長度

int len = env->GetArrayLength(strArr);

for(int i = 0;i< len;i++){

//獲取Object數組元素

jstring item = (jstring)env->GetObjectArrayElement(strArr,i);

const char * charStr = env->GetStringUTFChars(item, false);

LOGD("feifei strArray item:%s",charStr);

jstring jresult = env->NewStringUTF("HaHa");

//設置Object數組元素

env->SetObjectArrayElement(strArr,i,jresult);

env->ReleaseStringUTFChars(item,charStr);

}

}

3、JNI 訪問Java類的方法和字段

JNI 中訪問java類的方法和字段都是 通過反射來實現的。

JNI獲取Java類的方法ID和字段ID,都需要一個很重要的參數,就是Java類的方法和字段的簽名

image

JNI 中訪問Java對象的屬性 和方法:

java 類 TestNatvie.java

public class TestNatvie {

static {

System.loadLibrary("native-lib");

}

/**

* Jni調用 java 對象方法

*/

public native void testCallJavaMethod();

/**

* Jni 調用 java static 方法

*/

public native void testCallStaticJavaMethod();

/**

* JNI 訪問 java 的對象屬性和類屬性

* @param student

*/

public native void getJavaObjectField(Student student);

public void helloworld(String msg){

Log.d("feifei","hello world:"+msg);

}

public static void helloworldStatic(String msg){

Log.d("feifei","hello world:"+msg);

}

}

C++ 類 natvie-lib.cpp

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testCallJavaMethod(JNIEnv *env, jobject instance) {

//獲取類名

jclass clazz = env->GetObjectClass(instance);

if(clazz == NULL) return;

jmethodID javaMethod = env->GetMethodID(clazz,"helloworld","(Ljava/lang/String;)V");

if(javaMethod == NULL)return;

const char * msg = "nancy";

jstring jmsg = env->NewStringUTF(msg);

env->CallVoidMethod(instance,javaMethod,jmsg);

}

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testCallStaticJavaMethod(JNIEnv *env, jobject instance) {

//獲取java類型

jclass clazz = env->GetObjectClass(instance);

if(clazz == NULL) return;

jmethodID staticMethod = env->GetStaticMethodID(clazz,"helloworldStatic","(Ljava/lang/String;)V");

if(staticMethod == NULL) return;

jstring jmsg = env->NewStringUTF("wangfeng");

env->CallStaticVoidMethod(clazz,staticMethod,jmsg);

}

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_getJavaObjectField(JNIEnv *env, jobject instance,

jobject student) {

jclass clazz = env->GetObjectClass(student);

if(clazz == NULL )return;

// 獲取Object 實例屬性

jfieldID nameId = env->GetFieldID(clazz,"name","Ljava/lang/String;");

jstring jname = (jstring)env->GetObjectField(student,nameId);

jfieldID ageId = env->GetFieldID(clazz,"age","I");

jint jage = env->GetIntField(student,ageId);

const char * name = env->GetStringUTFChars(jname,false);

env->ReleaseStringUTFChars(jname,name);

//獲取java 類屬性:

jfieldID gradeId = env->GetStaticFieldID(clazz,"grade","I");

jint jgrade = env->GetStaticIntField(clazz,gradeId);

jfieldID nickeNameID = env->GetStaticFieldID(clazz,"nickname","Ljava/lang/String;");

jstring jnickname = (jstring)env->GetStaticObjectField(clazz,nickeNameID);

const char * nickeName = env->GetStringUTFChars(jnickname, false);

env->ReleaseStringUTFChars(jnickname,nickeName);

LOGD("feifei name:%s,age:%d,grade:%d,nickname:%s",name,jage,jgrade,nickeName);

//JNI 設置 java對象屬性

env->SetObjectField(student,nameId,env->NewStringUTF("張三"));

//JNI 設置 java 類屬性

env->SetStaticObjectField(clazz,nickeNameID,env->NewStringUTF("小白"));

jstring jnameNew = (jstring)env->GetObjectField(student,nameId);

jstring jnickNameNew = (jstring)env->GetStaticObjectField(clazz,nickeNameID);

const char * newName = env->GetStringUTFChars(jnameNew, false);

const char *newNickName = env->GetStringUTFChars(jnickNameNew, false);

env->ReleaseStringUTFChars(jnameNew,newName);

env->ReleaseStringUTFChars(jnickNameNew,newName);

LOGD("feifei after update name:%s,age:%d,grade:%d,nickname:%s",newName,jage,jgrade,newNickName);

}

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

Java代碼的內存是由垃圾回收器來管理,而JNI代碼則不受Java的垃圾回收器來管理。所以JNI代碼提供了一組函數,來管理通過JNI代碼生成的JNI對象,比如jobject,jclass,jstring,jarray等。

JNI對象的局部引用

在JNI接口函數中引用JNI對象的局部變量,都是對JNI對象的局部引用,一旦JNI接口函數返回,所有這些JNI對象都會被自動釋放。

不過我們也可以采用JNI代碼提供的DeleteLocalRef函數來刪除一個局部JNI對象引用。

//聲明局部變量clazz

jclass clazz = env->GetObjectClass(instance);

//手動釋放 局部變量 clazz ;DeleteLocalRef 也可不用手動調用,JNI方法返回之后,會自動釋放局部JNI變量

env->DeleteLocalRef(clazz);

JNI對象的全局引用

JNI對象的全局引用分為兩種,一種是強全局引用,這種引用會阻止Java的垃圾回收器回收JNI代碼引用的Java對象,另一種是弱全局引用,這種全局引用則不會阻止垃圾回收器回收JNI代碼引用的Java對象。

1、強全局引用

NewGlobalRef用來創建強全局引用的JNI對象

DeleteGlobalRef用來刪除強全局引用的JNI對象

2、弱全局引用

NewWeakGlobalRef用來創建弱全局引用的JNI對象

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

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

Java類 TestNatvie.java

/**

* 測試 JNI 強全局引用 和弱全局引用

*/

public native void testJNIReference(Object object);

C++ 代碼 natvie-lib.cpp

/**

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

* (2)對于JNI對象,絕對不能簡單的聲明一個全局變量,在JNI接口函數里面給這個全局變量賦值這么簡單,一定要使用JNI代碼提供的管理JNI對象的函數.

* JNI 全局引用分為兩種: 一種全局引用,這種引用會阻止Java垃圾回收器回收JNI代碼引用的對象;

* 另一種是弱全局引用,這種全局引用不會阻止垃圾回收器回收JNI 代碼引用的Java對象

* - NewGlobalRef用來創建強全局引用的JNI對象

* - DeleteGlobalRef用來刪除強全局引用的JNI對象

* - NewWeakGlobalRef用來創建弱全局引用的JNI對象

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

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

*/

jobject gThiz; //全局JNI對象引用

jobject gWeakThiz;//全局JNI對象弱應用

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testJNIReference(JNIEnv *env, jobject instance,jobject obj) {

//聲明局部變量clazz

jclass clazz = env->GetObjectClass(instance);

//手動釋放 局部變量 clazz ;DeleteLocalRef 也可不用手動調用,JNI方法返回之后,會自動釋放局部JNI變量

env->DeleteLocalRef(clazz);

//---- 強全局變量

gThiz = env->NewGlobalRef(obj);//生成全局的JNI 對象引用,這樣生成的全局的JNI對象 才可以在其他函數中使用

env->DeleteGlobalRef(gThiz);//在我們不需要gThis這個全局JNI對象應用時,可以將其刪除。

//---- 全局弱引用

gWeakThiz = env->NewWeakGlobalRef(obj);//生成全局的JNI對象引用,這樣生成的全局的JNI對象才可以在其它函數中使用

if(env->IsSameObject(gWeakThiz,NULL)){

LOGD("全局弱引用 已經被釋放了");

}

//釋放 全局弱應用對象

env->DeleteWeakGlobalRef(gWeakThiz);

}

5、JNI 進程間同步

JNI可以使用Java對象進行線程同步

MonitorEnter函數用來鎖定Java對象

MonitorExit函數用來釋放Java對象鎖

Java類 TestNative.java

/**

* JNI 利用 java 對象進行線程同步

* @param lock

*/

public native void testJNILock(Object lock);

C++ 類 native-lib.cpp

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testJNILock(JNIEnv *env, jobject instance,

jobject lock) {

//加鎖

env->MonitorEnter(lock);

//doSomething

LOGD("feifei, this is in lock");

//釋放鎖

env->MonitorExit(lock);

}

6、JNI異常相關的函數

JNI處理Java異常

當JNI函數調用的Java方法出現異常的時候,并不會影響JNI方法的執行,但是我們并不推薦JNI函數忽略Java方法出現的異常繼續執行,這樣可能會帶來更多的問題。我們推薦的方法是,當JNI函數調用的Java方法出現異常的時候,JNI函數應該合理的停止執行代碼。

ExceptionOccurred函數用來判斷JNI函數調用的Java方法是否出現異常

ExceptionClear函數用來清除JNI函數調用的Java方法出現的異常

/**

* 1、env->ExceptionOccurred() 判斷JNI調用java方法 是否遇到了Exception

* 2、env->ThrowNew() JNI 可以主動拋出Java Exception異常

*/

public native void testJavaException();

C++ 類 native-lib.cpp

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testJavaException(JNIEnv *env, jobject instance) {

jclass clazz = env->GetObjectClass(instance);

if(clazz == NULL) return;

jmethodID helloException_method = env->GetMethodID(clazz,"helloException","()V");

if(helloException_method == NULL )return;

env->CallVoidMethod(instance,helloException_method);

if(env->ExceptionOccurred() != NULL){

// env->ExceptionDescribe();

env->ExceptionClear();

LOGD("feifei,調用java 方法時 遇到了Exception");

return;

}

LOGD("feifei,調用helloException 方法成功了!");

LOGD("feifei,now JNI throw java exception - beging");

jclass expetionClazz = env->FindClass("java/lang/Exception");

if(expetionClazz == NULL) return;

env->ThrowNew(expetionClazz,"this is a exception");

}

JNI拋出Java類型的異常

JNI通過ThrowNew函數拋出Java類型的異常

Java類 TestNative.java

LOGD("feifei,now JNI throw java exception - beging");

jclass expetionClazz = env->FindClass("java/lang/Exception");

if(expetionClazz == NULL) return;

env->ThrowNew(expetionClazz,"this is a exception");

四 JNI 和 Java對象的互相持有

Java對象持久化C/C++對象實例

通常的做法是 將C++對象指針 強轉為jlong 類型,保存在調用者java對象的long型變量中,一直持有。

當需要使用該C++對象時,從Java對象中的long變量,強轉化為C++對象,進而使用。

TestNative.java

public class TestNatvie {

static {

System.loadLibrary("native-lib");

}

/**

* 用戶保存 C++對象的引用

*/

private long mNatvieId;

/**

* Java 對象持有 C++對象

*/

public native void initSDK();

/**

* Java 對象釋放 C++對象

*/

public native void releasSDK();

}

native-lib.cpp

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_initSDK(JNIEnv *env, jobject instance) {

Person * person = new Person();

person->setAge(18);

person->initSDK();

jclass classzz = env->GetObjectClass(instance);

jfieldID fid = env->GetFieldID(classzz,"mNatvieId","J");

//將C++對象的地址綁定到Java變量中

env->SetLongField(instance,fid,(jlong)person);

}

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_releasSDK(JNIEnv *env, jobject instance) {

jclass objectClass = env->GetObjectClass(instance);

jfieldID fid = env->GetFieldID(objectClass,"mNatvieId","J");

//取出java對象中保存的C++對象地址

jlong p = env->GetLongField(instance,fid);

//轉換成 C++對象

Person * person = (Person*)p;

person->releaseSDK();

//釋放person C++對象

free(person);

env->SetLongField(instance,fid,-1);

}

C/C++ 持久化Java對象

一般做法是

在本地方式中,創建一個全局引用 保存java對象:

env->NewGlobalRef(obj);

這樣在其他的JNI方法中就可以任意的使用該java對象了。

在不需要改java對象時,再將JNI全局引用刪除即可。

env->DeleteGlobalRef(gThiz);

使用示例:

TestNative.cpp

/**

* 利用JNI全局引用持有java 對象

*/

public native void testJNIReference(Object object);

natvie-lib.cpp

jobject gThiz; //全局JNI對象引用 - 用于持有特定的java對象。

jobject gWeakThiz;//全局JNI對象弱應用

extern "C"

JNIEXPORT void JNICALL

Java_com_example_feifei_testjni_TestNatvie_testJNIReference(JNIEnv *env, jobject instance,jobject obj) {

//---- 強全局變量

gThiz = env->NewGlobalRef(obj);//生成全局的JNI 對象引用,這樣生成的全局的JNI對象 才可以在其他函數中使用

env->DeleteGlobalRef(gThiz);//在我們不需要gThis這個全局JNI對象應用時,可以將其刪除。

}

參考文章

總結

以上是生活随笔為你收集整理的jni java_JNI 常见用法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。