Android本地開發工具箱(Native Development Kit,NDK)是Android SDK的輔助工具。如果你使用NDK創建本地代碼,則你的應用仍然需要打包到.apk文件中並在設備的虛擬機上運行。基本的Android應用模式沒有改變。
設置NDK環境
為了使用NDK,首先必須安裝和設置SDK。安裝和使用NDK的系統需求如下:
·Windows XP(32位)或Vista(32位或64位),包含Cygwin 1.7版本或更新,Mac OS X 10.4.8版本或更新,Linux(32位或64位)
·GNU Make 3.8.1或更新
·GNU AWK或nawk
首先,下載並安裝NDK(http://developer.android.com/sdk/ndk/index.html)。NDK的安裝很簡單:把NDK解壓到任何目錄下。這裡,我們把它解壓到ndk目錄。根目錄下會包含NDK的版本號。這裡,我們把該目錄命名為ndk。如果當前可用的版本號比我們在本章使用的版本號更新,你會發現其支持更多的本地API。
一旦下載並安裝完NDK,就可以找到很多文檔(在ndk/docs目錄下)。強烈建議你閱讀這些文檔,從文件OVERVIEW.html開始。NDK中還提供了一些示例(在ndk/samples目錄下)。這些示例涵蓋的範圍遠遠超出本章,因此當你對NDK稍有認識後,我們建議你查看並運行這些示例。
在Eclipse中編輯C/C++代碼
要充分利用Eclipse來編寫C語言代碼,需要安裝Eclipse C/C++開發工具,即Eclipse CDT。該工具可以在Eclipse環境中提供C語言代碼編輯器,和Eclipse的Java編輯功能類似,它也提供語法高亮、格式化以及其他高級功能。
要使用哪個CDT庫的版本取決於你的Eclipse版本。對於Eclipse Indigo,在Install New Software對話框中輸入庫http://download.eclipse.org/tools/cdt/releases/indigo/。關於如何使用Eclipse的更多說明,請參考第5章的為Eclipse添加包以及使用靜態分析器兩節的內容。
使用NDK編譯
為了使用NDK開發本地代碼,需要執行如下操作:
1.在項目中創建jni目錄。
2.把源代碼放到jni目錄中。
3.在jni目錄下創建Android.mk文件(或者Application.mk文件)。
4.在jni目錄下運行ndk/ndk-build命令。
可選的Application.mk文件描述了應用需要什麼樣的本地模塊,以及要編譯的具體ABI類型。關於Application.mk的更多信息,可查看文檔中的APPLICATION-MK.html文件。Application.mk示例文件如下:
# Build both ARMv5TE and ARMv7-A machine code. APP_ABI := armeabi armeabi-v7a # What platform to build against (android-3 (1.5) - android-9 (2.3)) APP_PLATFORM := android-9
Android.mk文件是描述編譯系統的源文件。它實質上是一個小的GNU Makefile文件,當編譯應用時由編譯系統解析它。關於Android.mk的更多信息,可查看文檔中的ANDROID-MK.html文件。Android.mk示例文件如下所示:
# Must define the LOCAL_PATH and return the current dir LOCAL_PATH := $(call my-dir) # Cleans various variables... making a clean build include $(CLEAR_VARS) # Identify the module/library\'s name LOCAL_MODULE := sample # Specify the source files LOCAL_SRC_FILES := sample.c # Load local libraries (here we load the log library) LOCAL_LDLIBS := -llog # Build the shared library defined above include $(BUILD_SHARED_LIBRARY)
Android.mk、Application.mk及本地源文件都準備好之後,可以在項目路徑下運行ndk/ndk-build來編譯你的庫文件。只要編譯成功,共享庫會被複製到應用的根項目路徑中,並被添加到應用的構建之中。
如果你下載並查看Android源代碼,會發現在Android的編譯系統中一直使用類似以上給出的makefile文件。熟悉JNI、本地代碼以及本地代碼如何在Android中編譯的最好方式是自己編寫一個使用Android NDK的簡單應用。
JNI、NDK和SDK:應用示例
為了幫助你理解SDK和本地源代碼具體是如何結合在一起的,我們提供了一個示例應用,其名稱為SampleActivityWithNativeMethods,是一個活動。Android的manifest文件片段如下:
<activity android:name=\".SampleActivityWithNativeMethods\" android:label=\"Sample Activity With Native Methods\" android:debuggable=\"true\" />
這個SampleActivityWithNativeMethods示例應用使用的佈局如下所示:
<?xml version=\"1.0\" encoding=\"utf-8\"?> <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:orientation=\"vertical\" android:layout_ android:layout_ > <Button android:id=\"@+id/whatami\" android:layout_ android:layout_ android:paddingTop=\"5dp\" android:paddingBottom=\"5dp\" android:text=\"What CPU am I?\" /> </LinearLayout>
示例C語言庫的源代碼中實現的是一個名為whatAmI的方法,我們的Java activity會通過whatami ID hook到按鈕上。還定義了一個函數,名為LOGINFO,歸結為__android_log_print調用。以下是Android log示例:
// the jni library MUST be included #include <jni.h> // the log lib is included #include <android/log.h> // usage of log #define LOGINFO(x...) __android_log_print(ANDROID_LOG_INFO,\"SampleJNI\",x) jstring Java_com_oreilly_demo_android_pa_ndkdemo_SampleActivityWithNativeMethods_whatAmI( JNIEnv* env,jobject thisobject) { LOGINFO(\"SampleJNI\",\"Sample Info Log Output\"); return (*env)->NewStringUTF(env, \"Unknown\"); }
示例應用的Android.mk文件如下所示。注意,它會加載log庫:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := sample LOCAL_SRC_FILES := sample.c LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
最後,是Java activity SampleActivityWithNativeMethods的源代碼。該類加載示例庫,並聲明本地方法whatAmI。當單擊按鈕時,會調用whatAmI方法,並返回「Unknown」,然後顯示字符串「CPU:Unknown」。如果你覺得輸出的信息量很低,不要著急,在下一節中會添加CPU信息:
package com.oreilly.demo.android.pa.ndkdemo; import com.oreilly.demo.android.pa.ndkdemo.R; import android.widget.Toast; public class SampleActivityWithNativeMethods extends Activity { static { System.loadLibrary(\"sample\"); // load our sample lib } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sample); setupview; } public native String whatAmI; // sample lib native method private void setupview { findViewById(R.id.whatami).setOnClickListener( new View.OnClickListener { public void onClick(View v) { String whatami = whatAmI; Toast.makeText(getBaseContext, \"CPU: \"+whatami, Toast.LENGTH_SHORT).show; } }); }