讀古今文學網 > Android程序設計:第2版 > Android NDK >

Android NDK

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;
            }
        });
    }