我们知道,Java native方法的注册形式有两种,一种是主动注册,一种是默认的被动注册,如果我们希望弄清楚java native方法的调用过程,我们首先就需要搞清楚两种注册方式的实现原理,下面我们先分别看看这两种注册方式。

一、Java native方法的主动注册

首先说说JNINativeMethod,它是一个结构体,表示了我们需要注册的本地方法,主要是将java方法跟native方法进行了一个对应,也可以理解为一种映射。

第一个变量name是Java中函数的名字。
第二个变量signature,表示函数的参数和返回值
第三个变量fnPtr是函数指针,指向native层的C函数

 

所以可以看到JNINativeMethod表示的就是java native函数以及其对应的native层的JNI函数。

 

下面我们来看看具体的注册过程,在进行native方法注册的时候,其实调用的就是JNIEnv的RegisterNatives函数。

我们在进行java native方法注册的时候,由于一个java类可能包含有多个native方法,所以在注册的时候将需要注册的方法放在一个数组中,上面代码的作用就是遍历这个数组,然后调用dvmRegisterJNIMethod方法来对每个方法进行一一的注册。

可以看到,JNI方法注册实际上是将Dalivk里面表示方法的Method对象的nativeFunc成员指向bridge函数,如果不打开CheckJni开关的话,bridge函数就是dvmCallJNIMethod,将Method对象的insns成员指向实际的native方法。具体我们可以看看dvmSetNativeFunc函数。

看到这里我们就可以知道,注册的过程就是将Method的nativeFunc指向dvmCallJNIMethod,将Method的insns成员指向实际的native方法,其实,当java层调用native函数的时候会进入dvmCallJNIMethod函数,而真正的native函数指针则存储在Method->insns中。dvmCallJNIMethod函数会先准备参数,再调用dvmPlatformInvoke执行对应的native方法,也就是method->insns所指向的方法,这样就完成了native函数的调用。

下面来看看dvmCallJNIMethod方法。

正如上面所说,dvmCallJNIMethod函数准备完参数之后,就会调用dvmPlatformInvoke来执行具体的native层的JNI方法,也就是method->insns所指向的方法。dvmPlatformInvoke通过libffi进行JNI方法调用,主要为了屏蔽Dalvik虚拟机运行在不同目标平台的细节。下面就不展开了。 

二、Java native方法的被动注册

JNI函数也可以不主动注册,而是根据一定的规则进行命名,然后由Dalvik自己去查找对应的native方法。对于这类JNI方法,Dalvik在进行类加载类时会默认将对应的Method对象的nativeFunc成员设置为dvmResolveNativeMethod函数地址。
前面我们讲过Dalvik虚拟机中Java类的加载过程,但是分析到loadClassFromDex的时候就结束了,并没有展开分析Java类的具体加载过程,下面我们来继续看看loadClassFromDex方法。

 

上面的代码中,我们重点关注java类方法的加载过程,下面看看loadMethodFromDex方法。

从上面代码可以看到,在dalvik中,当加载类并解析其中的函数时,如果标记为native函数,则会把Method->nativeFunc设置为dvmResolveNativeMethod
所以,当我调用对应native函数的时候,如果这个方法不是经过主动注册的,那么首先会执行dvmResolveNativeMethod方法。下面我们来看看该方法。

如果不是主动注册的话,native层的JNI方法需要按照一定的命名规则进行命名,这个具体就展开了,使用过JNI应该都清楚,dvmResolveNativeMethod首先遍历查找所有已加载的so,根据对应的JNI方法的名称来查找对应的JNI方法,如果找到该方法之后,再调用dvmUseJNIBridge函数设置Method的nativeFunc和insns成员,这个函数在主动注册已经介绍过了,执行完这个函数之后,Method的nativeFunc就是指向dvmCallJNIMethod函数,接着我们可以看到它执行的是method->nativeFunc指向的函数,也就是dvmCallJNIMethod函数,所以最终还是调用dvmCallJNIMethod,再由dvmCallJNIMethod执行相应的JNI方法。
从上面整个过程我们可以知道,不管是主动注册的java native方法还是被动注册的java native方法,它们的调用过程最终都是通过dvmCallJNIMethod这个bridge函数来实现对具体JNI方法的调用的。下面把上面过程通过画图表示如下:

三、作用

1、定位到java native方法对应的JNI方法
2、拦截java native方法的调用

 

总结

在Android中,不管是java方法还是java native方法,它在虚拟机中对应的都是一个Method对象,如果这个方法是一个java native方法的话,那么Method对象的nativeFunc会指向一个bridge函数,这个函数为dvmCallJNIMethod,当我们在调用java native函数的时候就会进入这个bridge函数,bridge函数的作用就是调用该java native方法对应的JNI方法。所以,我们可以这么理解,所有java native函数的执行都是通过一个名为dvmCallJNIMethod的bridge函数来实现对应JNI方法的调用的。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐