Android的JNI調用,AndroidJNI調用
Android提供NDK開發包來提供Android平台的C++開發,用來擴充Android SDK的功能。主要包括Android NDK構建系統和JNI實現與原生代碼通訊兩部分。
一、Android NDK構建系統
Android NDK的構建系統是基於GNU Make的。Android GNU Make系統除了需要一些內部的GNU片段外,還需要兩個檔案:Android.mk和Application.mk。Android NDK源碼給了很多的例子,以HelloJni為例,Android.mk源碼:
#Android.mk必須以LOCAL_PATH變數開頭
LOCAL_PATH := $(call my-dir)#清除除了LOCAL_PATH以外的LOCAL_<name>變數,例如LOCAL_MODULE與LOCAL_SRC_FILES等include $(CLEAR_VARS)#每一個原生組件被稱為一個模組LOCAL_MODULE := hello-jni
#源檔案LOCAL_SRC_FILES := hello-jni.c#編譯為共用庫,即尾碼名為.soinclude $(BUILD_SHARED_LIBRARY)
Application.mk源碼:
APP_ABI := all
一般選擇APP_ABI := armeabi-v7a就夠了
為了建立可供主應用程式使用的模組,必須將該模組變成共用庫。按照上述必不可少的步驟,可以繼續編譯多個共用庫。
Android也可以編譯靜態庫(尾碼名為.a),但是實際的Android應用程式並不直接使用靜態庫,並且應用程式套件組合中也不包含靜態庫。靜態庫可以用來構建共用庫。但是,當靜態庫與多個共用庫相連時,應用程式套件組合中會包含靜態庫的多個副本,徒增應用程式套件組合的大小。這種情況下,可以不構建靜態庫,而是將通用模組作為共用庫建立起來,動態串連相依模組以消除重複的副本。
……
……
……
Prebuilt庫
共用模組編譯時間要求有原始碼,為此Android提供了Prebuilt庫,以下場合,Prebuilt庫是非常有用的:
其他構建系統變數:
LOCAL_CFLAGS:一組可選的編譯器標誌,在編譯C和C++源檔案的時候會被傳送給編譯器;
LOCAL_CPP_FLAGS:一組可選的編譯器標誌,在只編譯C++源檔案時被傳送給編譯器;
LOCAL_LDLIBS:連結標誌的可選列表,它主要用於傳送要進行動態連結的系統庫列表。如連結日誌庫:
LOCAL_LDFLAGS += -llog
APP_CPPFLAGS:編譯器標誌,在編譯任何模組的C++源檔案時這些標誌都會被傳送給編譯器。
nkd-build指令碼命令:
ndk-build –C /project pathndk-build –Bndk-build clean
二、JNI實現與原生代碼通訊2.1 C/C++ 標頭檔產生器:javah
JDK內建一個名為javah的命令列工具,該工具由Java類檔案的原始定義產生原生函數名及其參數列表。C/C++源檔案只需要包含這個標頭檔並提供原生方法實現。
Javah的參數列表如下:
C:\Users\jiayayao>javah用法: javah [options] <classes>其中, [options] 包括: -o <file> 輸出檔案 (只能使用 -d 或 -o 之一) -d <dir> 輸出目錄 -v -verbose 啟用詳細輸出 -h --help -? 輸出此訊息 -version 輸出版本資訊 -jni 產生 JNI 樣式的標題檔案 (預設值) -force 始終寫入輸出檔案 -classpath <path> 從中載入類的路徑 -cp <path> 從中載入類的路徑 -bootclasspath <path> 從中載入引導類的路徑<classes> 是使用其全限定名稱指定的(例如, java.lang.Object)。
產生標頭檔的命令列參數如下:
javah -classpath bin/classes com.example.hellojni.Hellojni
Java方法stringFromJNI不帶任何參數,但是原生方法帶兩個參數:
這裡注意C與C++代碼稍有不同,C代碼如下:
return (*env)->NewStringUTF(env, "Hello from JNI ! ");
C++代碼如下:
return env->NewStringUTF("Hello from JNI ! ");
這是因為C++代碼中,JNIEnv實際上是一個C++類執行個體,JNI函數以成員函數的形式存在,因此JNI方法調用不要求JNIEnv執行個體作參數。
2.2 資料類型
Java有兩種資料類型:基礎資料型別 (Elementary Data Type)和引用資料類型:基礎資料型別 (Elementary Data Type)中Java/JNI/C++的映射關係如下:
參考型別的類型映射關係如下:
參考型別以不透明的引用方式傳遞給原生代碼,而不是以原生資料類型的的形式呈現,因此參考型別不能直接使用和修改。JNI提供了與這些參考型別密切相關的一組API。
Get<Type>ArrayRegion函數將給定的基本Java數組複製到給定的C數組中,如程式:
從C數組向Java數組提交所作的修改,如程式:
原生方法的記憶體配置超出了虛擬機器的管理範圍,且不能用虛擬機器的記憶體回收行程回收原生方法中的記憶體。
原生代碼回到Java損耗效能,建議將所有需要的參數傳遞給原生代碼調用,而不是讓原生代碼回到Java中。
GetStringUTFChars
ReleaseStringUTFChars