概述
上篇学习了NDK相关知识,这篇继续学习JNI相关知识,这篇文章仅作为笔记,以防以后忘记
JNI的数据类型和类型描述符
在JNI开发中,java的数据类型并不能直接在JNI上直接使用,需要有一定的转化,比如java中的int在JNI中就是jint,下面我们来学习下数据类型
基本数据类型
引用数据类型
其实大部分就是在java的数据类型的前面加上了小写j
来表示jni的数据类型
类型描述符
在JVM虚拟机中,存储数据类型名称的时候,是使用指定的描述符来使用,而不是我们使用的int,float来储存
表示一个string类
就是L
+全类名
,其中.
换成/
,最后加上;
表示数组
表示方法
JNIEnv 和 JavaVM
JavaVM
javaVM是java虚拟机在jni层的代表,一个进程只有一个JavaVM,所有的线程共用一个JavaVM
我们来看下JavaVM结构体
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
实现的,我们看下这个结构体
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来操作这个对象
参考:
https://juejin.im/post/5d19bf3f6fb9a07eaa229349
/p/87ce6f565d37
/afei__/article/details/80986203