JNI 调用流程 与 JNIEvn逻辑概述

一、JNI调用流程

JNI 调用流程 与 JNIEvn逻辑概述_第1张图片
如图所示,Java要想调用Native,首先的一个问题是,我调用的Java方法将要调用的是哪个c++方法?
这个工作是由JVM来负责的,接到Java的调用请求后,JVM通过事先维护好的“Java方法”与“Native方法”的对应关系,来找到对应的Native方法。
找到对应的Native方法后,再调用这个Native方法。

第二个问题,那么这个“Java方法”与“Native方法”的对应关系是在哪里维护的呢?
Java中加载so库(System.loadLibrary(“xxx.so”))后,会执行一个JNI_OnLoad方法,来完成动态注册的工作,这个动态注册就是维护“Java方法”与“Native方法”的对应关系的。其具体实现是通过一个类名为“JNINativeMethod”结构体做为数据承载的:

typedef struct {
	const char* name; // java方法名
	const char* signature; // java方法二进制表示法
	void* fnPtr; // C函数指针
} JNINativeMethod

一目了然了。

二、JNIEvn 到底是个啥 ?

就是字面意思 JNI环境,JNIEvn主要作用就是:提供了一系列的JNI函数来实现调用 Java方法、操作jobject对象等。提供了 Native反调Java 的一种途径。以下是它的一部分代码:
JNI 调用流程 与 JNIEvn逻辑概述_第2张图片

JNIEvn是由JVM创建的,与线程相关。
在JNI环境下,可以通过以下两种方法,获取一个JNIEvn

(*jvm) -> AttachCurrentThread(jvm, (void**)&evn, NULL)
(*jvm) -> GetEnv(jvm, (void**)&evn, JNI_VERSION_1_2)

三、JNIEvn与JVM、线程的关系是怎么样的?

首先要明白,JVM是一个多线程运行的环境。Java方法调用Native,Native也相应地处于一个多线程运行的环境。那么如何表现上面的逻辑关系呢?

捋一下结构:
一个JVM里同时运行了多个线程,也就是Thread,JNIEvn作为Thread的一个线程本地变量的形式存在,也就是 TLS(Thread Local Storage)变量。它的通常写法如下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
类比我们的 JNIEvn ,伪代码可以写成这样:
ThreadLocal<JNIEvn> sJNIEnvLocal = new ThreadLocal<JNIEvn>();

也就是说多线程对应多个JNIEvn,这样就清晰一些了,JNIEvn是Native调用 Java的桥梁。通过上面和Thread的关系的分析,更准确地说,一个JNIEvn是在一个线程的环境下,使Native可以调用java方法。也就是下图红框部分:
JNI 调用流程 与 JNIEvn逻辑概述_第3张图片

四、JNIEnv 与 so加载

JNI 调用流程 与 JNIEvn逻辑概述_第4张图片
看图说话,也就是说,对于同一个so库来说,在不同的线程加载后,生成的JNIEnv是不同的。Native组件开发者 在写Native代码时,可以通过将JNIEnv作为参数传入方法中,判断JNIEnv的不同,而避免多线程访问同一资源冲突的问题,来实现线程安全。请参考以下代码:

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEvn* env = NULL;
	if (register_android_media_MediaPlayer(env) < 0) { // 将JNIEnv作为参数传入方法
	}
}

register_android_media_MediaPlayer(JNIEvn* env){
	// 检查是否有其他很执行程序进入此物件,如果没有,则进入执行
	if ((*env) -> MonitorEnter(env, obj) != JNI_OK) {
		// do sth
	}
}

你可能感兴趣的:(Android虚拟机(JVM),jvm,jni,JNIEnv)