上篇學習了NDK相關知識,這篇繼續學習JNI相關知識,這篇文章僅做爲筆記,以防之後忘記java
在JNI開發中,java的數據類型並不能直接在JNI上直接使用,須要有必定的轉化,好比java中的int在JNI中就是jint,下面咱們來學習下數據類型數組
Java數據類型 | jni數據類型 | 描述 |
---|---|---|
boolean | jboolean | 無符號char類型 |
byte | jbyte | 帶符號8位整形 |
char | jchar | 無符號的16位整形 |
short | jshort | 帶符號的16位整形 |
int | jint | 帶符號的32位整形 |
long | jlong | 帶符號的64位整形 |
float | jfloat | 32位浮點型 |
java類型 | JNI類型 | 描述 |
---|---|---|
java.lang.Object | jobject | 能夠表示任何java對象 |
java.lang.String | jstring | 字符串對象 |
java.lang.Class | jclass | class對象 |
Object[] | jobjectArray | java任何對象數組的表現形式 |
boolean[] | jbooleanArray | 布爾類型的數組 |
byte[] | jbyteArray | byte數組的表現形式 |
char[] | jcharArray | char數組的表現形式 |
short[] | jshortArray | short數組的表現形式 |
int[] | jintArray | int數組的表現形式 |
long[] | jlongArray | long數組的表現形式 |
float[] | jfloatArray | float數組的表現形式 |
double[] | jdoubleArray | double數組的表現形式 |
java.lang.Throwable | jthrowable | java throwable的表現形式 |
void | void | 無類型 |
其實大部分就是在java的數據類型的前面加上了小寫j
來表示jni的數據類型函數
在JVM虛擬機中,存儲數據類型名稱的時候,是使用指定的描述符來使用,而不是咱們使用的int,float來儲存post
java類型 | 類型描述符 |
---|---|
int | I |
long | J |
byte | B |
short | S |
char | C |
float | F |
double | D |
boolean | Z |
void | V |
其餘引用數據類型 | L+全類名+; |
數組 | [ |
方法 | (參數)返回值 |
類型 | 描述 |
---|---|
java類型 | java.lang.String |
jni描述符 | Ljava/lang/String; |
就是L
+全類名
,其中.
換成/
,最後加上;
學習
類型 | 描述 |
---|---|
java類型 | String[] |
jni描述符 | [Ljava/;ang/String; |
java類型 | int[][] |
jni描述符 | [[I |
類型 | 描述 |
---|---|
java類型 | long f (int n,String s,int arr[]); |
jni描述符 | (ILjava/lang/String;[I)J |
java類型 | void f (); |
jni類型 | ()V |
javaVM是java虛擬機在jni層的表明,一個進程只有一個JavaVM,全部的線程共用一個JavaVMthis
咱們來看下JavaVM結構體spa
struct _JavaVM {
const struct JNIInvokeInterface* functions;
#if defined(__cplusplus)
jint DestroyJavaVM() { return functions->DestroyJavaVM(this); }
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args) { return functions->AttachCurrentThread(this, p_env, thr_args); }
jint DetachCurrentThread() { return functions->DetachCurrentThread(this); }
jint GetEnv(void** env, jint version) { return functions->GetEnv(this, env, version); }
jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args) { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};
複製代碼
這裏值分析C++版的,能夠看到全部的方法都是JNIInvokeInterface
實現的,咱們看下這個結構體.net
struct JNIInvokeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
jint (*DestroyJavaVM)(JavaVM*);
jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
jint (*DetachCurrentThread)(JavaVM*);
jint (*GetEnv)(JavaVM*, void**, jint);
jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};
複製代碼
咱們看到JNIInvokeInterface
結構體包含5個函數,看方法名大概能知道他們有什麼做用,好比:GetEnv
函數獲取JNIEnv
指針線程
當從Java層到Native層開發時,他會自動建立JavaVM
對象,可是當Native層到Java層開發時,須要咱們主動建立JavaVM
對象,咱們能夠用下面的函數建立指針
jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);
第一個參數:指向JavaVM *的指針,函數調用成功會給JavaVM *賦值
第二個參數:指向JNIEnv *的指針,函數調用成功會給JNIEnv *賦值
第三個參數:是指向JavaVMInitArgs的指針,是初始化虛擬機的參數
複製代碼
JNIEnv是一個線程相關的結構體,該結構體表明瞭java在本線程的執行環境
JavaVM:JavaVM是java虛擬機在jni層的表明,全局只有一個 JNIEnv:每一個線程都有一個,jni可能有多個JNIEnv
native環境建立線程,若是要訪問jni,須要調用AttachCurrentThread
方法進行關聯,使用DetachCurrentThread
接觸關聯
在_JavaVM
結構體中,有一個方法getEnv()
能夠獲取JNIEnv
jint GetEnv(JavaVM *vm, void **env, jint version);
第一個參數:JavaVM虛擬機對象
第二個參數:指向JNIEnv的指針
第三個參數:jni的版本,根據jdk的版本,目前有四種值,分別爲 JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6
複製代碼
函數調用成功會給JNIEnv賦值
咱們先看下JNIEnv
結構體
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion() { return functions->GetVersion(this); }
jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); }
jclass FindClass(const char* name) { return functions->FindClass(this, name); }
jmethodID FromReflectedMethod(jobject method) { return functions->FromReflectedMethod(this, method); }
jfieldID FromReflectedField(jobject field) { return functions->FromReflectedField(this, field); }
...省略不少函數
}
複製代碼
咱們能夠看到函數的實現最終仍是交給了JNINativeInterface
去實現,咱們再看下這個結構體
struct JNINativeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
void* reserved3;
jint (*GetVersion)(JNIEnv *);
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);
jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
jclass (*GetSuperclass)(JNIEnv*, jclass);
jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
jint (*Throw)(JNIEnv*, jthrowable);
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
jthrowable (*ExceptionOccurred)(JNIEnv*);
void (*ExceptionDescribe)(JNIEnv*);
void (*ExceptionClear)(JNIEnv*);
void (*FatalError)(JNIEnv*, const char*);
...省略不少的方法
}
複製代碼
這個結構體的做用是操做java層的入口,具體有倆個做用