不同編程語言之間的函數的關聯

一、JNI(Java Native Interface, Java本地調用)

作用:在Java程序中的函數可以調用C/C++編寫的函數;

           在C/C++程序中的函數可以調用Java編寫的函數;

JNI實例

Java(MediaScanner)<——>JNI(libmedia_jni.so)<——>Native(libmedia.so)

在Java層中的MediaScanner類中有一些函數需要由Native層來實現;

MediaScanner將通過JNI庫libmedia_jni.so和Native層的libmedia.so交互;

從上面的分析可知,JNI層必須實現爲動態庫的形式,這樣Java虛擬機才能加載和調用 它的函數;

Java層的MediaScanner分析

MediaScanner.java

public class MediaScanner
{
    static {
        /*1)加載對應的JNI庫,media_jni是JNI庫的名字,在實際加載動態庫的時候將其拓展成libmedia_jni.so,windows平臺上拓展爲media_jni.dll*/
        System.loadLibrary("media_jni");
        native_init();  //調用native_init函數
    }
    ......
    //2)聲明一個native函數,native爲Java關鍵字,表示它將由JNI層完成。
    private static native final void native_init();
    ......
    private native void processFile(String path, String mimeType, MediaScannerClient client);
    ......
}

上面的代碼列出了兩個要點:一個是加載JNI庫,另一個是Java的native函數。

加載JNI庫

原則上在調用native函數前,都可以加載;通常在類的static語句中加載;

調用System.loadLibrary()方法;

從上面的代碼可以發現,native_init和processFile函數前都有Java的關鍵字native,它表示這兩個函數將由JNI層來實現;

JNI層MediaScanner的分析

MediaScanner的JNI層代碼在android_media_MediaScanner.cpp中

android_media_MediaScanner.cpp

//native_init的JNI實現
static void android_media_MediaScanner_native_init(JNIEnv *env)
{
    ......
}
//processFile的JNI層實現
static void android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client)
{
    ......
}

Java層native_init函數如何對應JNI層的android_media_MediaScanner_native_init函數?

註冊JNI函數

native_init函數位於android.media這個包中,它的全路徑名應該是android.media.MediaScanner.native_init,而JNI層函數的名字是android_media_MediaScanner_native_init;

JNI層中把Java函數名稱中的“.”換成“_”,通過這種方式native_init找到JNI層的對應函數android_media_MediaScanner_native_init;

1、靜態方法

使用Java的工具程序javah找對應的JNI函數,流程如下:

先編寫Java代碼,然後編譯生成.class文件。

使用Java工具程序javah,$ javah -o output packagename.classname

之後生成output.h的JNI層頭文件,其中packagename.classname是Java代碼編譯後的class文件,在output.h中聲明瞭對應的JNI層函數,只要實現裏面的函數即可。

如MediaScanner對應JNI層頭文件就是android_media_MediaScanner.h

對應關係:

當Java層調用native_init函數時,它會從對應的JNI庫中尋找Java_android_media_MediaScanner_native_init函數,如果沒有就報錯。如果找到就爲這兩個函數建立一個關聯關係,就是保存JNI層函數的函數指針,以後再調用就可以使用這個函數指針了,這些工作是由虛擬機完成的。

2、動態註冊

JNI中使用JNINativeMethod結構保存上面Java和JNI層函數的一一對應的關係;

//定義一個JNINativeMethod數組,其成員就是Java層中所有的native函數的一一對應關係

static JNINativeMethod gMethods[] = {
    ......
    {
        "processFile"  //Java中native函數的函數名
        //processFile的簽名信息,簽名信息由函數參數及返回值構成;
        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
        (void *)android_media_MediaScanner_processFile  //JNI層對應的函數指針
    },
    ......
    {
        "native_init",
        "()V",
        (void *)android_media_MediaScanner_native_init
    },
};
//註冊JNINativeMethod數組
int register_android_media_MediaScanner(JNIEnv *env)
{
    //調用AndroidRuntime的registerNativeMethods函數,第二個參數表明是Java中的哪個類
    return AndroidRuntime::registerNativeMethods(env, "andorid/media/MediaScanner", gMethods, NELEM(gMethods));
}

AndroidRunTime提供了一個registerNativeMethods函數來完成註冊工作,下面看registerNativeMethods的實現,代碼如下:

int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethod, int numMethods)
{
    //調用jniRegisterNativeMethods函數完成註冊
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
其中jniRegisterNativeMethods是Android平臺爲了方便JNI使用而提供的一個幫助函數,代碼如下:

int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    ......
    //實際上是調用JNIEnv的RegisterNatives函數完成註冊的
    if((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return -1;
    }
    return 0;
}

動態註冊的工作只用兩個函數就能完成。

jclass clazz = (*env)->FindClass(env, className);
(*env)->RegisterNatives(env, clazz, gMethods, numMethods);

動態註冊的函數,當Java層通過System.loadLibrary加載完JNI動態庫後,緊接着會查找該庫中一個叫JNI_OnLoad的函數,調用它完成註冊;

建議實現這個JNI_OnLoad函數,在其中做一些初始化的工作;

函數簽名信息的生成

java提供了一個javap的工具可以幫助生成函數和變量的簽名信息;

$ java -s -p xxx

其中xxx爲編譯生成的class文件,s表示輸出內部數據類型的簽名信息,p表示打印所有函數和成員的簽名信息,默認只會答應public的成員和函數的簽名信息;

二、XPCOM組件




發佈了23 篇原創文章 · 獲贊 40 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章