1. 程式人生 > >Dalvik虛擬機器中Java native方法的呼叫過程

Dalvik虛擬機器中Java native方法的呼叫過程

我們知道,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方法的呼叫的。

歡迎關注我的公眾號:DroidMind

精品內容,獨家釋出