Android JNI and NDK learning (two): JNIEnv and JavaVM

Overview

In the last article, I learned about NDK, and this article will continue to learn about JNI. This article is only a note, in case you forget it in the future

JNI data types and type descriptors

In JNI development, the data types of Java cannot be used directly on JNI, and need to be converted. For example, the int in Java is jint in JNI. Let’s learn about the data types.

Basic data type

Java data types jni data type description
boolean jboolean Unsigned char type
byte jbyte Signed 8-bit integer
char jchar Unsigned 16-bit integer
short jshort Signed 16-bit integer
int jit Signed 32-bit integer
long jlong Signed 64-bit integer
float jfloat 32-bit floating point

Reference data type

java type JNI type description
java.lang.Object jobject Can represent any java object
java.lang.String jstring String object
java.lang.Class jclass class object
Object[] jobjectArray The manifestation of any array of objects in java
boolean[] jbooleanArray Boolean array
byte[] jbyteArray Representation of byte array
char[] jcharArray The representation of char array
short[] jshortArray The representation of the short array
int[] jintArray Representation of int array
long[] jlongArray The representation of the long array
float[] jfloatArray The representation of the float array
double[] jdoubleArray The representation of the double array
java.lang.Throwable jthrowable The manifestation of java throwable
void void No type

In fact, most of the data types are added lowercase in front of the data types of java jto indicate the data types of jni

Type descriptor

In the JVM virtual machine, when storing the data type name, it uses the specified descriptor instead of the int, float we use to store

java type Type descriptor
int I
long J
byte B
short S
char C
float F
double D
boolean WITH
void V
Other reference data types L+full class name+;
Array [
method (Parameter) return value

Represents a string class

Types of description
java type java.lang.String
jni descriptor Ljava/lang/String;

Is L+ 全类名, which is .replaced by /, and finally added

Represents an array

Types of description
java type String[]
jni descriptor [Ljava/; ang/String;
java type int[][]
jni descriptor [[I

display method

Types of description
java type long f (int n,String s,int arr[]);
jni descriptor (ILjava / lang / String; [I) J
java type void f ();
jni type () V

JNIEnv 和 JavaVM

JavaVM

javaVM is the representative of the java virtual machine at the jni layer. There is only one JavaVM for a process, and all threads share one JavaVM.

Let's take a look at the JavaVM structure

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*/
};

The value here is analyzed in the C++ version, you can see that all the methods are JNIInvokeInterfaceimplemented, let’s look at this structure

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*);
};

We see that the JNIInvokeInterfacestructure contains 5 functions, you can probably know what they do by looking at the method name, such as: GetEnvfunction to get JNIEnvpointer

When developing from the Java layer to the Native layer, he will automatically create JavaVMobjects, but when developing from the Native layer to the Java layer, we need to actively create JavaVMobjects. We can use the following function to create

jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);

第一个参数:指向JavaVM *的指针,函数调用成功会给JavaVM *赋值

第二个参数:指向JNIEnv *的指针,函数调用成功会给JNIEnv *赋值

第三个参数:是指向JavaVMInitArgs的指针,是初始化虚拟机的参数

JNIEnv

JNIEnv is a thread-related structure, which represents the execution environment of java in this thread

JavaVM: JavaVM is the representative of the java virtual machine at the jni layer. There is only one
JNIEnv globally : each thread has one, and jni may have multiple 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来操作这个对象

参考:

https://juejin.im/post/5d19bf3f6fb9a07eaa229349

https://www.jianshu.com/p/87ce6f565d37

https://blog.csdn.net/afei__/article/details/80986203

Guess you like

Origin blog.csdn.net/qq_34760508/article/details/106517498