android NDK簡介
開始之前
本指南假設您已熟悉原生程式設計 Android 開發內在的概念。
簡介
本節簡要說明 NDK 的工作方式。Android NDK 是一組允許您將 C 或 C++(“原生程式碼”)嵌入到 Android 應用中的工具。 能夠在 Android 應用中使用原生程式碼對於想執行以下一項或多項操作的開發者特別有用:
- 在平臺之間移植其應用。
- 重複使用現有庫,或者提供其自己的庫供重複使用。
- 在某些情況下提高效能,特別是像遊戲這種計算密集型應用。
工作方式
本節介紹在為 Android 構建原生應用時使用的主要元件,並且描述構建和封裝的過程。
主要元件
在構建應用時,您應該已經瞭解以下元件:
- ndk-build:ndk-build 指令碼用於在 NDK 中心啟動構建指令碼。這些指令碼:
- 自動探測您的開發系統和應用專案檔案以確定要構建的內容。
- 生成二進位制檔案。
- 將二進位制檔案複製到應用的專案路徑。
- Java:Android 構建過程從 Java 來源生成
.dex
(Dalvik EXecutable) 檔案,這些檔案是 Android OS 在 Dalvik 虛擬機器(“DVM”)中執行的檔案。 即使您的應用根本未包含任何 Java 原始碼,構建過程仍會生成原生元件在其中執行的.dex
可執行檔案。開發 Java 元件時,使用
native
關鍵字指示以原生程式碼形式實現的方法。 例如,以下函式宣告向編譯器告知實現在原生庫中:public native int add(int x, int y);
- 原生共享庫:NDK 從原生原始碼構建這些庫或
.so
注:如果兩個庫使用相同的簽名實現各自的方法,就會發生關聯錯誤。 在 C 語言中,“簽名”只表示方法名稱。在 C++ 中,“簽名”不僅表示方法名稱,還表示其引數名稱和型別。
- 原生靜態庫:NDK 也可構建靜態庫或
.a
檔案,您可以關聯到其他庫。
- Java 原生介面 (JNI):JNI 是 Java 和 C++ 元件用以互相溝通的介面。 本指南假設您具備 JNI 知識;如需瞭解相關資訊,請查閱 Java 原生介面規範。
- 應用二進位制介面 (ABI):ABI 可以非常精確地定義應用的機器程式碼在執行時如何與系統互動。 NDK 根據這些定義構建
.so
檔案。 不同的 ABI 對應不同的架構:NDK 包含對 ARMEABI(預設)、MIPS 和 x86 的 ABI 支援。 如需瞭解詳細資訊,請參閱
- 清單:如果您要編寫沒有 Java 元件的應用,必須在清單中宣告 類。原生
Activity 和應用在“使用
native_activity.h
介面”下提供瞭如何執行此操作的詳細資訊。
下面兩個專案僅在使用 ndk-build
指令碼構建時以及使用 ndk-gdb
指令碼除錯時才需要。
Android.mk
:必須在jni
資料夾內建立Android.mk
配置檔案。ndk-build
指令碼將檢視此檔案,其中定義了模組及其名稱、要編譯的原始檔、版本標誌以及要連結的庫。
Application.mk
:此檔案列舉並描述您的應用需要的模組。 這些資訊包括:- 用於針對特定平臺進行編譯的 ABI。
- 工具鏈。
- 要包含的標準庫(靜態和動態 STLport 或預設系統)。
流程
為 Android 開發原生應用的一般流程如下:
- 設計應用,確定要在 Java 中實現的部分,以及要以原生程式碼形式實現的部分。
注:雖然可以完全避免 Java,但您可能發現,Android Java 框架對於包括控制顯示和 UI 在內的任務很有用。
- 像建立任何其他 Android 專案一樣建立一個 Android 應用專案。
- 如果要編寫純原生應用,請在
AndroidManifest.xml
中宣告 類。 如需瞭解詳細資訊,請參閱原生 Activity 和應用。 - 在“JNI”目錄中建立一個描述原生庫的
Android.mk
檔案,包括名稱、標誌、連結庫和要編譯的原始檔。 - 或者,也可以建立一個配置目標 ABI、 工具鏈、發行/除錯模式和 STL 的
Application.mk
檔案。對於其中任何您未指明的專案,將分別使用以下預設值:- ABI:armeabi
- 工具鏈:GCC 4.8
- 模式:發行
- STL:系統
- 將原生來源置於專案的
jni
目錄下。 - 使用 ndk-build 編譯原生(
.so
、.a
)庫。 - 構建 Java 元件,生成可執行
.dex
檔案。 - 將所有內容封裝到一個 APK 檔案中,包含
.so
、.dex
以及應用執行所需的其他檔案。
原生 Activity 和應用
Android SDK 提供幫助程式類 ,可用於寫入完全原生的
Activity。 可處理 Android 框架與原生程式碼之間的通訊,因此您不必為其建立子類或呼叫其方法, 只需在 AndroidManifest.xml
檔案中宣告要設為原生的應用,然後開始建立原生應用。
使用 的 Android 應用仍會在其自己的虛擬機器中執行,與其他應用以沙盒分隔。 因此,您仍可通過 JNI 訪問 Android 框架 API。 但在某些情況下 – 例如對於感測器、輸入事件和資產– NDK 提供可以使用的原生介面,而無需通過 JNI 呼叫。 如需瞭解有關此類支援的詳細資訊,請參閱 Android NDK 原生 API。
無論是否要開發原生 Activity,我們都建議使用傳統 Android 構建工具建立專案。 這樣有助於確保 Android 應用的構建和封裝都使用正確的結構。
Android NDK 為實現原生 Activity 提供兩個選項:
native_activity.h
標頭定義 類的原生版本。 其中包含建立原生 Activity 所需的的回撥介面和資料結構。 由於應用的主執行緒處理回撥,因此不得阻止回撥的實現。 否則可能收到 ANR(應用未響應)錯誤,因為主執行緒在回撥返回之前會無響應。android_native_app_glue.h
檔案定義基於native_activity.h
介面構建的靜態幫助程式庫。它將派生另一個執行緒,用於處理事件迴圈中的回撥或輸入事件。 將這些事件移至單獨的執行緒可防止任何回撥阻止您的主執行緒。
<ndk_root>/sources/android/native_app_glue/android_native_app_glue.c
來源也可用,允許您修改實現。
如需瞭解有關如何使用此靜態庫的詳細資訊,請檢查原生 Activity 示例應用及其文件。<ndk_root>/sources/android/native_app_glue/android_native_app_glue.h
檔案中的註釋也提供其他閱讀材料。
使用 native_activity.h 介面
要使用 native_activity.h
介面實現原生 Activity,請執行以下操作:
- 在專案的根目錄中建立一個
jni/
目錄。此目錄用於儲存所有原生程式碼。 - 在
AndroidManifest.xml
檔案中宣告原生 Activity。因為您的應用沒有 Java 程式碼,所以將
android:hasCode
設為false
。<application android:label="@string/app_name" android:hasCode="false">
必須將 Activity 標記的
android:name
屬性設定為 。<activity android:name="android.app.NativeActivity" android:label="@string/app_name">
meta-data
標記的android:value
屬性指定共享庫的名稱,其中包含應用的入口點(例如 C/C++main
),省略lib
字首和.so
字尾。<meta-data android:name="android.app.lib_name" android:value="native-activity" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
- 建立用於原生 Activity 的檔案,並實現
ANativeActivity_onCreate
變數中指定的函式。應用在原生 Activity 啟動時會呼叫此函式。 此函式類似於 C/C++ 中的main
,用於接收ANativeActivity
結構的指標,其中包含您需要寫入的各個回撥實現的函式指標。在ANativeActivity->callbacks
中設定回撥實現的適用回撥函式指標。 - 將
ANativeActivity->instance
欄位設定為要使用的特定資料的任何例項的地址。 - 實現您希望 Activity 在啟動時執行的任何其他操作。
- 實現您在
ANativeActivity->callbacks
中設定的其餘回撥。如需瞭解何時呼叫回撥的詳細資訊,請參閱管理 Activity 生命週期。 - 開發應用的其餘部分。
- 在專案的
jni/
目錄中建立Android.mk file
,向構建系統描述您的原生模組。 如需瞭解詳細資訊,請參閱 Android.mk。 - 在建立
Android.mk
檔案後,使用ndk-build
命令編譯原生程式碼。$ cd <path>/<to>/<project> $ <ndk>/ndk-build
- 像平常一樣構建和安裝 Android 專案。如果原生程式碼在
jni/
目錄中,構建指令碼會自動將從它構建的.so
檔案封裝到 APK 中。