一.概述
Hello Everyone焦辅,上一篇是教大家配置了開發(fā)環(huán)境梢莽,好多同學(xué)對(duì)里面的某些代碼可能還存在疑惑的,這篇文章就帶領(lǐng)代駕熟悉下JNI編程的一些語法知識(shí)盗忱。
在開篇之前我們看下状蜗,我們以上篇的例子來展開
通過javah生成的c++文件拟枚,可以看到一個(gè)很奇怪的方法瞧柔。這個(gè)就是我們今天的入門了薪夕。其中System.loadLibrary(“[庫名]”)桃纯,名字命名是有相應(yīng)的規(guī)則的酷誓,而這個(gè)庫名和CMakeList.txt文件中的庫名必須一致。Java虛擬機(jī)會(huì)找到這個(gè)類名并調(diào)用該函數(shù)态坦。
靜態(tài)注冊(cè)Native方法:
仔細(xì)看這個(gè)函數(shù)會(huì)發(fā)現(xiàn)方法名的組成為 Java_包名類名方法名盐数。其中JNIEXPORT和JNICALL是兩個(gè)宏定義,主要作用就是說明該函數(shù)為JNI函數(shù)伞梯,jstring是定義JNI定義的數(shù)據(jù)類型玫氢。在Java虛擬機(jī)加載的時(shí)候回連接對(duì)應(yīng)的Native方法,比如在例子中的 public native String getId(); 通過javah操作后會(huì)生成
JNIEXPORT jstring JNICALL Java_com_example_mylibrary_JNITest_getID();
動(dòng)態(tài)注冊(cè)Native方法:
JNI_ONLOAD函數(shù):該函數(shù)會(huì)有兩個(gè)參數(shù)谜诫,其中*jvm為Java虛擬機(jī)實(shí)例漾峡,JavaVM結(jié)構(gòu)體定義了以下函數(shù):
DestroyJavaVM
AttachCurrentThread
DetachCurrentThread
GetEnv
這里我們使用了GetEnv函數(shù)獲取JNIEnv變量,上面的JNI_ONLOAD函數(shù)有如下代碼:
JNIEnv *env;
if (jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
這里調(diào)用了GetEnv函數(shù)獲取JNIEnv結(jié)構(gòu)體指針喻旷,其實(shí)JNIEnv結(jié)構(gòu)體是指向一個(gè)函數(shù)表的生逸,該函數(shù)表指向了一個(gè)對(duì)應(yīng)的JNI函數(shù),我們通過調(diào)用這些JNI函數(shù)實(shí)現(xiàn)JNI編程且预。
獲取JAVA對(duì)象槽袄,完成動(dòng)態(tài)注冊(cè)
上面介紹 如何獲取JNIEnv結(jié)構(gòu)體指針,得到結(jié)構(gòu)體指針我們就可以調(diào)用JNIEnv中的RegisterNatives函數(shù)完成動(dòng)態(tài)注冊(cè)native方法:
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)
第一個(gè)參數(shù)是Java層對(duì)應(yīng)包含native方法的對(duì)象锋谐,指的就是JNITest對(duì)象了遍尺,通過調(diào)用JNIEnv對(duì)應(yīng)的函數(shù)獲取class對(duì)象
jclass clz = env->FindClass("com/github/songnick/jni/AndroidJni");
第二個(gè)參數(shù)是JNINativeMethod結(jié)構(gòu)體指針,這里 JNINative結(jié)構(gòu)體是描述Java層Native方法的怀估,定義如下:
typedef struct {
const char* name;//Java層native方法的名字
const char* signature;//Java層native方法的描述符
void* fnPtr;//對(duì)應(yīng)JNI函數(shù)的指針
} JNINativeMethod;
第三個(gè)參數(shù)為注冊(cè)native方法的數(shù)量狮鸭,一般會(huì)動(dòng)態(tài)注冊(cè)多個(gè)native方法合搅,首先會(huì)定義一個(gè)JNINativeMethod數(shù)組,然后將該指針作為RegisterNative函數(shù)的參數(shù)傳入
JNINativeMethod nativeMethod[] = {{"dynamicLog", "()V", (void*)nativeDynamicLog}};
最后調(diào)用RegisterNative函數(shù)完成動(dòng)態(tài)注冊(cè):
env->RegisterNatives(clz, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]));
JNIEnv結(jié)構(gòu)體
JNIEnv結(jié)構(gòu)體指向一個(gè)函數(shù)表歧蕉,該函數(shù)表指向一系列的JNI函數(shù)灾部,我們通過調(diào)用這些JNI函數(shù)可以與Java層進(jìn)行交互,以下是常用的函數(shù):
..........
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
jboolean GetBooleanField(jobject obj, jfieldID fieldID)
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
CallVoidMethod(jobject obj, jmethodID methodID, ...)
CallBooleanMethod(jobject obj, jmethodID methodID, ...)
..........
- GetFieldID函數(shù)是獲取Java對(duì)象中某個(gè)變量的ID
- GetBooleanField()函數(shù)是根據(jù)變量的ID獲取數(shù)據(jù)類型為Boolean
- GetMethodID函數(shù)獲取Java對(duì)象中對(duì)應(yīng)的方法ID
- CallVoidMethod根據(jù)methodID調(diào)用對(duì)應(yīng)對(duì)象中的方法惯退,并且該方法的返回值Void類型
- CallBooleanMethod根據(jù)methodID調(diào)用對(duì)應(yīng)對(duì)象中的方法赌髓,并且該方法的返回值Boolean類型
JNI數(shù)據(jù)類型
JNI的本質(zhì)是銜接Java和C++/C 相互通信的,因?yàn)镴ava的對(duì)象是不被C++識(shí)別的催跪,同樣的C++的指針也不被Java識(shí)別锁蠕,所以JNI需要自定義一些自己的數(shù)據(jù)類型來使雙方的通信保持順暢。
1.原始數(shù)據(jù)類型Java Type Native Typ Description
JavaType | Native | Description |
---|---|---|
boolean | jboolean | unsigned 8 bits |
byte | jbyte | signed 8 bits |
char | jchar | unsigned 16 bits |
short | jshort | signed 16 bits |
int | jint | signed 32 bits |
long | jlong | signed 64 bits |
float | jfloat | 32 bits |
double | jdouble | 64 bits |
void | void | N/A |
2.引用類型
前面我們?cè)讷@取AndroidJni對(duì)象的使用通過定義jclass引用懊蒸,然后調(diào)用FindClass函數(shù)獲取了該對(duì)象荣倾,所以JNI也定義了一些引用類型以便JNI層調(diào)用,具體的引用類型如下:
jobject (all Java objects)
|
|-- jclass (java.lang.Class objects)
|-- jstring (java.lang.String objects)
|-- jarray (array)
| |--jobjectArray (object arrays)
| |--jbooleanArray (boolean arrays)
| |--jbyteArray (byte arrays)
| |--jcharArray (char arrays)
| |--jshortArray (short arrays)
| |--jintArray (int arrays)
| |--jlongArray (long arrays)
| |--jfloatArray (float arrays)
| |--jdoubleArray (double arrays)
|
|--jthrowable
3.方法和變量的ID
?當(dāng)需要調(diào)用Java中的某個(gè)方法的時(shí)候我們首先要獲取它的ID骑丸,根據(jù)ID調(diào)用JNI函數(shù)獲取該方法舌仍,變量的獲取過程也是同樣的過程,這些ID的結(jié)構(gòu)體定義如下:
struct _jfieldID; /* opaque structure */
typedef struct _jfieldID *jfieldID; /* field IDs */
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */
描述符
1.類描述符
前面為了獲取Java的JNITest對(duì)象通危,是通過調(diào)用FindClass()函數(shù)獲取的铸豁,該函數(shù)參數(shù)只有一個(gè)字符串參數(shù),字符串如下:
>com/example/mylibrary/JNITest
這個(gè)就是我們JNI定義了對(duì)類的描述符菊碟,規(guī)則也很簡(jiǎn)單节芥,包名+類名,并用“/”替換“.”
2.方法描述符
前面我們動(dòng)態(tài)注冊(cè)native方法的時(shí)候結(jié)構(gòu)體JNINativeMethod中含有方法描述符逆害,就是確定native方法的參數(shù)和返回值头镊,
Method Descriptor | Java Language Type |
---|---|
"()Ljava/lang/String;" | String f(); |
"(ILjava/lang/Class;)J" | long f(int i, Class c); |
"([B)V" | String f(byte[] bytes); |
上面的栗子我們看到方法的返回類型和方法參數(shù)有引用類型以及boolean、int等基本數(shù)據(jù)類型魄幕,對(duì)于這些類型的描述符在下個(gè)部分介紹拧晕。這里數(shù)組的描述符以"["和對(duì)應(yīng)的類型描述符來表述。對(duì)于二維數(shù)組以及三維數(shù)組則以"[["和"[[["表示:
Descriptor | Java Langauage Type |
---|---|
"[[I" | int[][] |
"[[[D" | double[][][] |
3.數(shù)據(jù)類型描述符
?前面我們說了方法的描述符梅垄,那么針對(duì)boolean、int等數(shù)據(jù)類型描述符是怎樣的呢输玷,JNI對(duì)基本數(shù)據(jù)類型的描述符定義如下:
Descriptor | Java Langauage Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
Field Desciptor Java Language Type
Z boolean
B byte
C char
S short
I int
J long
F floa
D double
Descriptor | Java Langauage Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
對(duì)于引用類型描述符是以"L"開頭";"結(jié)尾队丝,示例如下所示:
Field Desciptor Java Language Type
"Ljava/lang/String;" String
"[Ljava/lang/Object;"
今天的學(xué)習(xí)就到這里了,該篇文章只是介紹了下基本的語法知識(shí)以及jni通信的基本方法欲鹏。后續(xù)我們接著進(jìn)行~