Android平臺Native開發與JNI機制
JNI的出現使得開發者既可以利用Java語言跨平臺、類庫豐 富、開發便捷等特點,又可以利用Native語言的高效。
JNI是JVM實現中的一部分,因此Native語言和Java程式碼都執行在JVM的宿主環境。
JNI是一個雙向的介面:開發者不僅可以通過JNI在Java程式碼中訪問Native模組,還可以在 Native程式碼中嵌入一個JVM,並通過JNI訪問運行於其中的Java模組。可見,JNI擔任了一個橋樑的角色,它將JVM與Native模組聯絡起 來,從而實現了Java程式碼與Native程式碼的互訪。在OPhone上使用Java虛擬機器是為嵌入式裝置特別優化的Dalvik虛擬機器。每啟動一個應 用,系統會建立一個新的程序執行一個Dalvik虛擬機器,因此各應用實際上是執行在各自的VM中的。Dalvik VM對JNI的規範支援的較全面,對於從JDK 1.2到JDK 1.6補充的增強功能也基本都能支援。
缺點:由於Native模組的使用,Java程式碼會喪失其原有的跨平臺性和型別安全等特性。此外,在JNI應用中,Java程式碼與Native代 碼運行於同一個程序空間內;對於跨程序甚至跨宿主環境的Java與Native間通訊的需求,可以考慮採用socket、Web Service等IPC通訊機制來實現。
互的型別可以分為在Java程式碼中呼叫Native模組和在Native程式碼中呼叫Java模組兩種。
Java呼叫Native模組
HelloJni.java
- /* A native method that is implemented by the
- * 'hello-jni' native library, which is packaged
- * with this application.
- */
- public native String stringFromJNI();
這個stringFromJNI()函式就是要在Java程式碼中呼叫的Native函式。
hello-jni.c
- #include <string.h>
- #include <jni.h>
- jstring
- Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
- jobject thiz ) {
- return (*env)->NewStringUTF(env, "Hello from JNI !");
- }
這個Native函式對應的正是我們在com.example.hellojni.HelloJni這個中宣告的Native函式String stringFromJNI()的具體實現。
JNI函式的命名規則:
Java程式碼中的函式宣告需要新增native 關鍵 字;Native的對應函式名要以“Java_”開頭,後面依次跟上Java的“package名”、“class名”、“函式名”,中間以下劃線“_”
分割,在package名中的“.”也要改為“_”。此外,關於函式的引數和返回值也有相應的規則。對於Java中的基本型別如int 、double 、char 等,在Native端都有相對應的型別來表示,如jint 、jdouble 、jchar 等;其他的物件型別則統統由jobject 來表示(String 是個例外,由於其使用廣泛,故在Native程式碼中有jstring 這個型別來表示,正如在上例中返回值String 對應到Native程式碼中的返回值jstring )。而對於Java中的陣列,在Native中由jarray 對應,具體到基本型別和一般物件型別的陣列則有jintArray 等和jobjectArray 分別對應(String 陣列在這裡沒有例外,同樣用jobjectArray 表示)。還有一點需要注意的是,在JNI的Native函式中,其前兩個引數JNIEnv *和jobject 是必需的——前者是一個JNIEnv 結構體的指標,這個結構體中定義了很多JNI的介面函式指標,使開發者可以使用JNI所定義的介面功能;後者指代的是呼叫這個JNI函式的Java物件,有點類似於C++中的this 指標。在上述兩個引數之後,還需要根據Java端的函式宣告依次對應新增引數。在上例中,Java中宣告的JNI函式沒有引數,則Native的對應函式只有型別為JNIEnv *和jobject 的兩個引數。
,要使用JNI函式,還需要先載入Native程式碼編譯出來的動態庫檔案(在Windows上是.dll,在Linux上則為.so)。這個動作是通過如下語句完成的:
- static {
- System.loadLibrary("hello-jni");
- }
- TextView tv = new TextView(this);
- tv.setText( stringFromJNI() );
- setContentView(tv);
從OPhone的系統架構來看,JVM和Native系統庫位於核心之上,構成OPhone
Runtime;更多的系統功能則是通過在其上的Application Framework以Java
API的形式提供的。因此,如果希望在Native庫中呼叫某些系統功能,就需要通過JNI來訪問Application Framework提供的API。
- package com.example.hellojni;
- public class SayHello {
- public String sayHelloFromJava(String nativeMsg) {
- String str = nativeMsg + " But shown in Java!";
- return str;
- }
- }
- jstring helloFromJava( JNIEnv* env ) {
- jstring str = NULL;
- jclass clz = (*env)->FindClass(env, "com/example/hellojni/SayHello");
- jmethodID ctor = (*env)->GetMethodID(env, clz, "<init>", "()V");
- jobject obj = (*env)->NewObject(env, clz, ctor);
- jmethodID mid = (*env)->GetMethodID(env, clz, "sayHelloFromJava", "(Ljava/lang/String;)Ljava/lang/String;");
- if (mid) {
- jstring jmsg = (*env)->NewStringUTF(env, "I'm born in native.");
- str = (*env)->CallObjectMethod(env, obj, mid, jmsg);
- }
- return str;
- }
如上這種使用NewObject建立的物件例項被稱為“Local
Reference”,它僅在建立它的Native程式碼作用域內有效,因此應避免在作用域外使用該例項及任何指向它的指標。如果希望建立的物件例項在作用 域外也能使用,則需要使用NewGlobalRef介面將其提升為“Global Reference”——需要注意的是,當Global Reference不再使用後,需要顯式的釋放,以便通知JVM進行垃圾收集。
Native模組的編譯與釋出
NDK的全稱是Native Development Toolkit,即原生應用開發包。,為廣大開發者提供了編譯用於Android應用的Native模組的能力,以及將Native模組隨Java應用打包為APK檔案。