内构函数java_Android JNI参数传递
Java中調用native函數傳遞的參數是Java數據類型,到了JNI層需進行數據類型轉換,基本數據類型是在前面加個j,如int——>jint,應用數據類型除了基本數據類型的數據、Class、String和Throwable外,其余所有Java對象的數據類型在JNI中都用jobject表示。
//Java層com.xxx.yyy包MyJni.java中的jnitest有三個參數
jnitest(String path, String name, MyJni mj);
//JNI層對應的函數的后三個參數與jnitest的參數對應
com_xxx_yyy_MyJni_jnitest(JNIEnv *env, jobject thiz, jstring path, jstring name, jobject mj);
若對象類型都用就object表示,就好比是native層的void* 類型一樣。第二個參數就object代表Java層的實例對象,如果Java層是static函數,則這個參數將是jclass,表示在調用哪個Java Class的靜態函數。
JNIEnv介紹
JNIEnv是一個與線程相關的代表JNI環境的結構體,內部結構如圖:
從上圖可知JNIEnv提供了一些JNI系統函數:
調用Java的函數
操作jobejct對象等很多事情
每個線程都有一個JNIEnv,由于線程相關,所以一個線程不能使用另一個線程的JNIEnv結構體。JNIEnv是native函數轉換成JNI層函數后有虛擬機傳進來的,但當后臺線程收到一個網絡消息,且需有native層函數主動回調Java層函數時,JNIEnv如何傳遞?
這就要用到JavaVM,它是虛擬機在JNI層的代表,如下:
//全進程只有一個JavaVM對象,可保存且在任何地方都可使用
jint JNI_OnLoad(JavaVM* vm, void* reserved);
而JavaVM 和 JNIEnv的關系如下:
調用JavaVM的AttachCurrentThread函數,就可得到這個線程的JNIEnv結構體,即可在后臺回調Java函數。
在后臺線程退出前,需調用JavaVM的DetachCurrentThread函數釋放對應的資源。
通過JNIEnv操作jobject
Java引用類型除了少數幾個外,其余在JNI層都會用jobject來表示對象的數據類型,操作jobject的本質是操作Java對象的成員變量和成員函數。
jfieldID 和 jmethodID 介紹
JNI規則中用jfieldID 和 jmethodID表示Java類的成員變量和成員函數,可通過JNIEnv的函數得到:
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);
jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);
其中,jclass表示Java類,name表示成員函數或成員變量的名字,sig為這個函數和變量的簽名信息。使用方法如下:
MyJni(JNIEnv *env, jobject mj)......
{
//先找到com.xxx.yyy.MyJni類在JNI層中對應的jclass實例。
jclass myJniInterface = env->FindClass("com.xxx.yyy.MyJni");
//取出MyJni類中函數jnitest的jMethodID。
mMyJniMethodID = env->GetMethodID(myJniInterface, "jnitest", "(Ljava/lang/String;JJ)V");
//取出MyJni類中函數jnitest1的jMethodID。
mJniTestMethodID = env->GetMethodID(myJniInterface, "jnitest1",
"(Ljava/lang/String;Ljava/lang/String;)V");
如果每次操作jobject前都去查詢jmethodID或jfieldID,將會影響程序的運行效率,故在初始化時可取出ID并保存起來供后續使用。
使用jfieldID 和 jmethodID
實例代碼如下:
virtual bool myjni(const char* path, long long lastModified, long long fileSize)
{
jstring pathstr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL)
return;
/*
調用JNIEnv的CallVoidMethod函數,注意CallVoidMethod的參數:
第一個是MyJni的jobject對象,
第二個是函數myjni的jmethodID,后面是Java中myjni的參數
*/
mEnv->CallVoidMethod(mClient, mMyJniMethodID, pathStr, lastModified, fileSize);
mEnv->DeleteLocalRef(pathStr);
return (!mEnv->ExceptionCheck());
通過JNIEnv輸出CallVoidMethod,再把jobject、jMethodID和對應的參數傳進去,JNI層就能調用Java對象的函數。
實際上JNIEnv輸出一系列類似CallVoidMethod的函數,形式如下:
NativeType CallMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...)
其中type對應Java函數的返回值類型,例如CallIntMethod、CallVoidMethod等。
上面是針對非static函數,如果調用Java的static函數,則用JNIEnv輸出的CallStatic< Type>Method系列函數。
通過jfieldID操作jobject的成員變量,如下:
//獲得filedID可調用GetField系列函數獲取jobject對應的成員變量值
nativeType GetField(JNIEnv *env, jobject obj, jfieldID fieldID)
//或調用SetField系列函數設置jobject對應的成員變量值
void SetField(JNIEnv *env, jobject obj, jfield fieldID, NativeType value)
JNI類型簽名介紹
static JNINativeMethod gMethods[] = {
......
{
"processFile"
"(Ljava/lang/String;Ljava/lang/String;Landroid/media/MyJni;)V",
(void*)com_xxx_yyy_MyJni
},
......
}
代碼中字符串”(Ljava/lang/String;Ljava/lang/String;Landroid/media/MyJni;)V”是Java中對應函數的簽名信息,有參數類型和返回值類型共同組成。Java支持函數重載,可定義同名但不同參數的函數,進根據函數名無法找到具體函數,故JNI技術中將參數類型和返回值類型作為函數的簽名信息。
JNI規范定義的函數簽名信息格式:
(參數1類型標示參數2類型標示...參數n類型標示)返回值類型標示
“(Ljava/lang/String;Ljava/lang/String;Landroid/media/MyJni;)V”,其中括號內是參數類型的標識,最右邊是返回值類型的標識,void類型對應的標識是V。當參數的類型是引用類型時,其格式是“L包名;”,其中包名中的“.”換成“/”。Ljava/lang/String表示是一個Java的String類型。
總結
以上是生活随笔為你收集整理的内构函数java_Android JNI参数传递的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: beserver.exe是什么进程 be
- 下一篇: beremote.exe进程是什么文件