Android JNI和NDK學習(二):JNIEnv 和 JavaVM

概述

上篇學習了NDK相關知識,這篇繼續學習JNI相關知識,這篇文章僅做爲筆記,以防之後忘記java

JNI的數據類型和類型描述符

在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+全類名+;
數組 [
方法 (參數)返回值

表示一個string類

類型 描述
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

JNIEnv 和 JavaVM

JavaVM

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

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的做用

咱們先看下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層的入口,具體有倆個做用

  • 調用java函數:使用JNIEnv調用java中的代碼
  • 操做java對象:java對象傳入jni層是jstring,能夠使用JNIEnv來操做這個對象

參考:

juejin.im/post/5d19bf…

www.jianshu.com/p/87ce6f565…

blog.csdn.net/afei__/arti…

相關文章
相關標籤/搜索