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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

实现一个在JNI中调用Java对象的工具类,从此只需一行代码

發(fā)布時間:2024/4/15 java 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 实现一个在JNI中调用Java对象的工具类,从此只需一行代码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

我們知道在jni中執(zhí)行一個java函數(shù)需要調(diào)用幾行代碼才行,如

jclass objClass = (*env).GetObjectClass(obj); jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig); jobject result = (*env).CallObjectMethod(obj, methodID, ...);

這樣使用起來很不方便,尤其當(dāng)需要大量的調(diào)用java函數(shù)就會產(chǎn)生大量的上述代碼,由此我產(chǎn)生了一個開發(fā)封裝這些操作的工具類,以便大量簡化我們的開發(fā)。

簡單封裝

其實可以看到整個過程基本是固定不變的:先獲取Class,然后獲取method,然后在執(zhí)行call。所以可以簡單的先封裝成一系列工具函數(shù),如:

jobject callObjMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);jobject result = (*env).CallObjectMethodV(obj, methodID, args);va_end(args);return result; }jint callIntMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);jint result = (*env).CallIntMethodV(obj, methodID, args);va_end(args);return result; }jboolean callBooleanMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);jboolean result = (*env).CallBooleanMethodV(obj, methodID, args);va_end(args);return result; }

這樣當(dāng)我們要通過jni執(zhí)行某個java函數(shù)的時候,就一行代碼就可以搞定了,比如String.length():

jint len = callIntMethod(env, str, "length", "()I")

這樣就可以大大減少了代碼量,而且代碼也更易讀了。

優(yōu)化

通過上面可以看到這些函數(shù)大部分代碼都非常類似,只有一行代碼和返回值有區(qū)別,所以我考慮使用函數(shù)模版來進(jìn)行優(yōu)化,如下:

template <typename T> T callMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);T result;if(typeid(T) == typeid(jobject)){result = (*env).CallObjectMethodV(obj, methodID, args);}if(typeid(T) == typeid(jdouble)){result = (*env).CallDoubleMethodV(obj, methodID, args);}...va_end(args);return *result; }

這樣只要調(diào)用callMethod<return type>即可,愿望很美好,但是上面代碼實際上是無法通過編譯。

因為模版函數(shù)實際上是在編譯時,根據(jù)調(diào)用的類型,拷貝生成多個具體類型的函數(shù)以便使用。

所以如果有這樣的調(diào)用callMethod<jobject>(...),在編譯時就會拷貝成一個如下的函數(shù):

jobject callMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);jobject result;if(typeid(jobject) == typeid(jobject)){result = (*env).CallObjectMethodV(obj, methodID, args);}if(typeid(jobject) == typeid(jdouble)){result = (*env).CallDoubleMethodV(obj, methodID, args);}...va_end(args);return *result; }

注意這行代碼:

if(typeid(jobject) == typeid(jdouble)){result = (*env).CallDoubleMethodV(obj, methodID, args); }

雖然實際上是無法執(zhí)行的代碼,但是編譯時還是會進(jìn)行檢查,由于將jdouble類型的賦值給jobject類型的result,所以編譯不通過,類型無法轉(zhuǎn)換。而且這里用強(qiáng)轉(zhuǎn)static_cast等方法都不行。

我考慮兩種方法來解決這個問題,一種是保證編譯不報錯,因為運(yùn)行時不會執(zhí)行的代碼,只要通過編譯就可以。另外一種是不同的類型編譯不同的代碼。

void指針

在c++中void指針可以被賦值任何類型指針,且void指針強(qiáng)轉(zhuǎn)為任何類型指針在編譯時不會報錯。代碼如下:

template <typename T> T callMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);T* result = new T();if(typeid(T) == typeid(jobject)){jobject objec = (*env).CallObjectMethodV(obj, methodID, args);void *p = &objec;result = (T*)p;}if(typeid(T) == typeid(jdouble)){jdouble doub = (*env).CallDoubleMethodV(obj, methodID, args);void *p = &doub;result = (T*)p;}va_end(args);return *result; }

當(dāng)然利用void指針很不安全,雖然可以通過編譯,但是執(zhí)行時如果類型不同會直接造成crash。所以并不建議這種方式。

模版函數(shù)特例化

將差異代碼部分封裝到另一個模版函數(shù)中,并且對每種類型進(jìn)行特例化,這樣還可以去掉if-else判斷,代碼如下:

template <typename K> K call2Result(JNIEnv *env, jobject obj, jmethodID methodID, va_list args){return *(new K()); }template <> jobject call2Result(JNIEnv *env, jobject obj, jmethodID methodID, va_list args){return (*env).CallObjectMethodV(obj, methodID, args); }template <> jdouble call2Result(JNIEnv *env, jobject obj, jmethodID methodID, va_list args){return (*env).CallDoubleMethodV(obj, methodID, args); } ...template <typename T> T callMethod(JNIEnv *env, jobject obj, const char *methodName, const char *methodSig, ...){va_list args;jclass objClass = (*env).GetObjectClass(obj);jmethodID methodID = (*env).GetMethodID(objClass, methodName, methodSig);va_start(args,methodSig);T result = call2Result<T>(env, obj, methodID, args);va_end(args);return result; }

這樣在編譯時,如果返回值是jobject類型的,當(dāng)編譯到call2Result時,就會直接調(diào)用jobject call2Result(...)這個函數(shù),就不再涉及類型轉(zhuǎn)換的問題。

這樣去掉了if判斷,但是由于沒有通用的函數(shù),所以所有使用的類型都需要特例化,如果某個類型未特例化,代碼執(zhí)行可能就會有問題。而在jni中,與java對應(yīng)的類型其實就那么十幾種,所以我們只要全部實現(xiàn)一遍call2Result即可。

undefined reference to

使用模版函數(shù)出現(xiàn)這個問題,是因為沒有將模版函數(shù)的實現(xiàn)寫在頭文件中,只將模版函數(shù)的聲明在頭文件中,而在源文件中實現(xiàn)的。

所以我們應(yīng)該將模版函數(shù)的實現(xiàn)也寫進(jìn)頭文件中,而模版函數(shù)特例化則可以在源文件中實現(xiàn),但是注意要include頭文件。

返回值是void類型

因為void的特殊性,所以如果當(dāng)成泛型來處理會有很多問題,這里把返回值是void類型的單獨(dú)實現(xiàn)一個函數(shù)即可。

總結(jié)

上面我們僅僅是實現(xiàn)了調(diào)用普通函數(shù)的工具,根據(jù)這個思路我們還可以實現(xiàn)調(diào)用靜態(tài)函數(shù)、獲取成員變量、賦值成員變量等,這樣當(dāng)我們在進(jìn)行jni開發(fā)的時候,如果需要對java對象或類進(jìn)行操作,只需要一行代碼就可以了。

源碼

關(guān)注公眾號:BennuCTech,發(fā)送“JNIObjectTools”獲取源碼。

總結(jié)

以上是生活随笔為你收集整理的实现一个在JNI中调用Java对象的工具类,从此只需一行代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。