摘要:使用可以獲取類的方法得到例如如果調用的是靜態方法需要使用獲取。那么方法簽名的規則又是怎么樣呢方法簽名在中第四個參數就是方法簽名,是支持重載的,所以需要標明方法的傳參和返回值,這就是方法的簽名。其中代表不傳參數,代表返回值為。
相對于NDK來說SDK里面有更多API可以調用,有時候我們在做NDK開發的時候,需要在JNI直接Java中的方法和變量,比如callback,系統信息等....
如何在JNI中調用Java方法呢?就需要先了解FindClass和GetMethodID了。
在JNI中可以通過FindClass可以找到Java類,得到jclass,例如:
jclass clz=(*env)->FindClass(env,"com/jjz/JniHandle");
FindClass的第二個參數需要傳入類的完整包名。
使用GetMethodID可以獲取類的方法,得到jmethodID,例如:
jmethodID getStringFromJava=(*env)->GetMethodID(env,class,"getStringForJava","()V");
如果調用的是靜態方法需要使用GetStaticMethodID獲取。通過FindeClass可以在JNI中找到需要調用的類,GetMethodID可以找到對應的方法,這樣就可以在JNI中調用Java的方法了。
在GetMethodID中,第四個參數是()V,這個是方法簽名。那么方法簽名的規則又是怎么樣呢?
在GetMethodID中第四個參數()V就是方法簽名,Java是支持重載的,所以需要標明方法的傳參和返回值,這就是方法的簽名。它是用來保證方法的唯一性。其中()代表不傳參數,V代表返回值為void。
方法簽名對于Java的類型都有一一對應的值。方法簽名中用大寫的字母對應了java的基本數據類型:
Z -> boolean
B -> byte
C -> char
S -> short
I -> int
J -> long
F -> float
D -> double
其實就是有兩個比較特殊的:boolean對應的是Z,long對應的J,其他的都是首個字母的大寫即可。
數組的表示方法,以[為標志,一個[標識一維數組,[[表示二維數組,例如:
byte[] -> [B
int[][] -> [[I
引用類型的表示方法,需要以L開頭,以;結束,中間對應類型的包名加類名,例如:
String -> Ljava/lang/String;
Object -> Ljava/lang/Object;
自定義類的表示方法,比如包名為jjz.example,類名為JniHandle的表示方法:
jjz.example.JniHandle ->Ljjz/example/JniHandle;
除了手動輸入類名和方法簽名以外,JDK還提供了直接生成方法簽名的工具javap。
在build之后可以在路徑../app/build/intermediates/classes/debug下可以找到build之后生成的.class文件,運行命令:
javap -s com/jjz/JniHandle
就可以得到這個類的所有的方法簽名:
Compiled from "JniHandle.java" public class com.jjz.JniHandle { ? public com.jjz.JniHandle(); ? ? descriptor: ()V ? public static java.lang.String getStringFromStatic(); ? ? descriptor: ()Ljava/lang/String; ? public java.lang.String getStringForJava(); ? ? descriptor: ()Ljava/lang/String; }
有了類的引用,和方法的簽名就可以直接在JNI中調用Java方法了,下面分別介紹下靜態方法和類方法的調用。
靜態方法的調用調用類的靜態方法,首先要得到類的引用,再調用類的靜態方法。
先定義一個類和靜態方法用來提供給JNI調用:
public class JniHandle { ? ? public static String getStringFromStatic() { ? ? ? ? return "string from static method in java"; ? ? } }
在定義了一個native方法com.jjz.NativeUtil.callJavaStaticMethodFromJni,生成這個方法的JNI代碼,在JNI代碼中調用JniHandle類的靜態方法:
JNIEXPORT void JNICALL Java_com_jjz_NativeUtil_callJavaStaticMethodFromJni(JNIEnv *env, jclass type) { ? ? jclass jniHandle = (*env)->FindClass(env, "com/jjz/JniHandle"); ? ? if (NULL == jniHandle) { ? ? ? ? LOGW("can"t find JniHandle"); ? ? ? ? return; ? ? } ? ? jmethodID getStringFromStatic = (*env)->GetStaticMethodID(env, jniHandle, "getStringFromStatic", ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "()Ljava/lang/String;"); ? ? if (NULL == getStringFromStatic) { ? ? ? ? (*env)->DeleteLocalRef(env, jniHandle); ? ? ? ? LOGW("can"t find method getStringFromStatic from JniHandle "); ? ? ? ? return; ? ? } ? ? jstring result = (*env)->CallStaticObjectMethod(env, jniHandle, getStringFromStatic); ? ? const char *resultChar = (*env)->GetStringUTFChars(env, result, NULL); ? ? (*env)->DeleteLocalRef(env, jniHandle); ? ? (*env)->DeleteLocalRef(env, result); ? ? LOGW(resultChar); }
在Java中調用com.jjz.NativeUtil.callJavaStaticMethodFromJni可以該方法可以在logcat中看到string from static method in java,這樣就完成了在JNI中調用了Java靜態方法。
類方法的調用調用類方法要更加的復雜一些,調用步驟:
通過findClass找到類
通過GetMethodID得到構造函數
通過調用構造函數得到一個類的實例
通過GetMethodID得到需要調用的方法
使用類的實例調用方法
先定義一個類方法:
public class JniHandle { ? ? public String getStringForJava() { ? ? ? ? return "string from method in java"; ? ? } }
再定義一個native方法:com.jjz.NativeUtil.callJavaMethodFromJni,生成該方法的JNI代碼,在JMI代碼中實現調用JniHandle的類方法getStringForJava,代碼如下:
JNIEXPORT void JNICALL Java_com_jjz_NativeUtil_callJavaMethodFromJni(JNIEnv *env, jclass type) { ? ? jclass jniHandle = (*env)->FindClass(env, "com/jjz/JniHandle"); ? ? if (NULL == jniHandle) { ? ? ? ? LOGW("can"t find jniHandle"); ? ? ? ? return; ? ? } ? ? jmethodID constructor = (*env)->GetMethodID(env, jniHandle, "", "()V"); ? ? if (NULL == constructor) { ? ? ? ? LOGW("can"t constructor JniHandle"); ? ? ? ? return; ? ? } ? ? jobject jniHandleObject = (*env)->NewObject(env, jniHandle, constructor); ? ? if (NULL == jniHandleObject) { ? ? ? ? LOGW("can"t new JniHandle"); ? ? ? ? return; ? ? } ? ? jmethodID getStringForJava = (*env)->GetMethodID(env, jniHandle, "getStringForJava", ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "()Ljava/lang/String;"); ? ? if (NULL == getStringForJava) { ? ? ? ? LOGW("can"t find method of getStringForJava"); ? ? ? ? (*env)->DeleteLocalRef(env, jniHandle); ? ? ? ? (*env)->DeleteLocalRef(env, jniHandleObject); ? ? ? ? return; ? ? } ? ? jstring result = (*env)->CallObjectMethod(env, jniHandleObject, getStringForJava); ? ? const char *resultChar = (*env)->GetStringUTFChars(env, result, NULL); ? ? (*env)->DeleteLocalRef(env, jniHandle); ? ? (*env)->DeleteLocalRef(env, jniHandleObject); ? ? (*env)->DeleteLocalRef(env, result); ? ? LOGW(resultChar); }
調用方法com.jjz.NativeUtil.callJavaMethodFromJni即可看到Java中的字符串傳遞給了JNI最后輸出到了Logcat上。
在上面的代碼中有一個方法叫做DeleteLocalRef,它的意思是釋放局部引用,Android VM釋放局部引用有兩種方法:
本地方法執行完畢之后VM自動釋放
通過調用DeleteLocalRef手動釋放
既然上面說了VM會自動釋放引用為什么還需要手動釋放呢?
其實某些局部變量會阻止它所引用的對象被GC回收,它所引用的對象無法被GC回收,自己本身也就無法被自動釋放,因此需要使用DeleteLocalRef。而這里使用了JNI Local Reference,在JNI中引用了Java對象,如果不使用DeleteLocalRef釋放的話,引用無法回收,就會造成內存泄露。
源代碼地址:https://github.com/jjz/androi...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64844.html
摘要:介紹了在中配置的開發環境開發環境配置,開發環境配置完成之后,就要寫一下著名的程序了。尤其是但是并不妨礙你使用其他語言,只要調用約定支持就可以了。是指定所在的目錄,項目成功之后,會在目錄里生成文件。是包名加上類名。 介紹了在Android Studio中配置NDK的開發環境:Android Studio NDK開發-環境配置,NDK開發環境配置完成之后,就要寫一下著名的Hello Wor...
閱讀 920·2021-11-16 11:45
閱讀 2126·2021-10-09 09:44
閱讀 1341·2019-08-30 14:03
閱讀 1127·2019-08-26 18:28
閱讀 3328·2019-08-26 13:50
閱讀 1715·2019-08-23 18:38
閱讀 3450·2019-08-23 18:22
閱讀 3589·2019-08-23 15:27