Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 基於Android2.3.5系統:JNI與HAL實例解析[一]

基於Android2.3.5系統:JNI與HAL實例解析[一]

編輯:關於Android編程

 

一:Android系統下JNI簡介

Android系統下的JNI的全稱是:Java Native Interface (JNI),JNI標准是java平台的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI 是本地編程接口,它使得在 Java 虛擬機 (VM) 內部運行的 Java 代碼能夠與用其它編程語言(如 C、C++ 和匯編語言)編寫的應用程序和庫進行交互操作。

二:為什麼Java要推出JNI

JNI在Android系統中有著廣泛的應用。Android系統底層都是C/C++實現的,上層提供的API都是Java的,Java通過JNI調用底層的實現。

 

Java世界虛擬機使用Native語言編寫的,虛擬機運行在具體的平台上,虛擬機本身無法做到與平台無關,jni可以對java層屏蔽不同平台操作的差異,這樣就能實現java本身平台無關特性。適應已經用Native語言實現的技術。一些效率的問題需要Native語言實現。

 

在Java中使用JNI主要是為了以上幾點而實現的,在Anroid中的API多媒體接口MediaPlayer類,其實底層通過JNI調用libmedia庫。由於JNI的存在可以讓我們重用很多已經存在C/C++的庫,省去了重復開發的麻煩,並且可以利用很多開源的庫(Android庫中就有很多開源庫,比如libjpeg,libpng等等),並且讓我們開發的程序更有效率(C/C++代碼發揮硬件最佳性能)。

三:Java中JNI調用過程

在Java的世界裡,JNI對於應用本身來說,可以看做一個代理模式,對於開始者來說,需要使用C/C++來實現一個代理程序來實際操作目標原生函數,Java程序中則是JVM通過加載並調用JNI程序間接地調用目標原生函數。其調用的示意圖如下:

\

圖一:Android下JNI調用過程(Load DLL,DLL根據不同的環境而定)

Android系統Java下JNI程序開發的一般操作步驟如下:

編寫JAVA中的調用類;用javah生成C/C++原生函數的頭文件;C/C++中調用需要的其他函數功能,實現原生函數;將項目依賴的所有原生庫和資源加到java項目的路徑中;生成JAVA程序;發布JAVA應用和so庫;

 

四:Android系統下JNI語法

Android系統Java下的JNI對於應用本身來說,可以看做一個代理模式,對於開始者來說,需要使用C/C++來實現一個代理程序來實際操作目標原生函數,基本的數據類型轉換,如下所示:

\

圖二:基本數據類型轉換表

備注:符號屬性是指在JNI環境下

引用數據類型轉換表,如下所示:

\

圖三:引用數據類型轉換表

五:Java下JNI注冊函數

Java下的JNI命名規則:JNI層需要將JAVA函數名稱(包括包名)中的“.”換成“_”,用這種方式可以找到自己JNI層的路徑以及文件。注冊Native函數有兩種方法:靜態注冊和動態注冊。靜態注冊靜態就是根據函數名來建立java和jni函數之間的關聯,而且要求jni層函數的名字必須遵循特定的格式,其缺點在於:需要編譯所有的java聲明的類;javah生成的jni層函數特別長;初次調用native函數時要根據名字搜索對應的jni層函數來建立關聯聯系,這樣影響效率。因此這裡只詳細的講解動態注冊的方式,Java native與JNI通過JNINativeMethod的結構來建立聯系,其結構內容如下:

 

typedef struce {   
    const char* name;        //java中native函數的名字,不用攜帶包的路徑  
    const char*signature;    //java函數的簽名信息,用字符串表示,是參數類型和返回值類型的組合 
    void *fnptr;             //JNI層對應的函數指針,注意他是void*的類型。 
} JNINativeMethod; 
用一個Anroid系統下的實例來感性的認識和說明一下,(請到網上去下載Anroid2.3.4的OK6410的Anroid代碼,跟本文的路徑一致,因為我自己把這個Android2.3.4的OK6410移植到Android2.3.5的平台上)路徑為android2.3.5/frameworksaseservices orlinx_pwm_jniPwmJniService.cpp,這是一個PWM控制的JNI,具體的如下所示:

 

\

 

圖四:JNINativeMethod方法 Android提供一個RegisterNatives(JNIEnv *env, Const char* className, Const JNINativeMethod* gMethods, int numMethods)來實現這個功能,其實還有一個在AndroidRuntime.cpp中也實現了一個RegisterNatives函數,兩者的功能是相同的,如下所示:

 

 

\ 我們來看看PwmJniService.cpp函數中的JNI動態注冊吧,如下所示:

 

\

在JNI_OnLoad函數中,會去調用register_forlinx_server_PwmService()這個函數,而這個函數的定義如下所示:

\

一起來看看這行代碼:static const char* const kClassName =forlinx_pwm_server/server/PwmService;,就是通過kClassName找到相應的庫的,而這個PwmService就是在Android2.3.5/frameworksaseservices orlinx_pwm_serverserverPwmService.java文件,如下所示:

\

也就是說Java層通過這種手段實現了Java層去調用C/C++層的庫,這樣就將Anroid底層給封裝起來了,而在Android的JNI層會去調用Android的HAL層,而HAL層調用的就是Linux的驅動!!

六:JNI的垃圾回收
Java中創建的對象最後由垃圾回收器來回收和釋放內存的,而對於JNI,直接對對象進行引用後不會增加引用計數值,當從JNI返回後在JNI引用的對象有可能被垃圾回收器回收,而在JNI下面記錄的指針有可能是野指針,為了解決此類問題,JNI規范提供如下三類引用:

 

Local Refrence:本地引用,在JNI層函數中使用的非全局變量都是采用Local Refrence,它包含函數調用時傳入的jobject和在JNI層函數中創建的jobject,其最大的特點是一旦JNI層函數返回,這些jobject就可能被垃圾回收了。Global Refrence:全局引用,這類引用如果程序不主動調用銷毀接口將一直駐留在內存中。Weak Global Refrence:弱全局引用,在使用過程中有可能被釋放,調用JNIEnv中IsSameObject函數來判斷是否仍然存在。 七:Java下JNI函數的簽名信息 Android系統下Jave的JNI的簽名Signature的書寫規則如下: ()中的字符表示參數,後面的則代表返回值,如“()V”就表示void func(), “(II)V”表示void func(int, int)等。具體的每個字符對應關系如下: \
如果java函數的參數是class,則以“L”開頭,以“;”結尾,中間是用“/”隔開的包及類名,對應的C函數名的參數則為jobject,一個例外是String類,其對應類為jstring。如下所示:
          Ljava/lang/String;    String     jstring
          Ljava/net/Socket;     Socket     jobject
以android2.3.5/frameworksaseservices orlinx_pwm_jniPwmJniService.cpp中的static const JNINativeMethod gMethods[]數組函數為例子,如下所示: \
選取forlinx_set_freq函數來說明JNI簽名的問題,在上圖中可以看到 { _set_freq, (I)Z, (void *)forlinx_set_freq }, 這樣的定義方式,對照上表中的簽名解析表格,我們就一目了然啦。forlinx_set_freq函數的定義如下所示: \
可以看出來forlinx_set_freq的返回值類型為:jboolean,而jboolean所代表的JNI簽名為:Z,而jint freq是forlinx_set_freq函數中需要設置的一個值,而jint所代表的JNI的簽名為:I 因此forlinx_set_freq函數的簽名信息為:(I)Z

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved