1. 程式人生 > >8步教你開啟Android之門 NDK入門教程

8步教你開啟Android之門 NDK入門教程

第0步:下載工具

好了,讓我們開始吧。你需要下載NDK。我們先開始下載,因為在下載的過程中你可以檢查一下確保你所需要用到的其餘工具的版本都正確。

從Android網站下載適合你的作業系統的NDK。

現在,對照下列檢查你的工具版本:

如果在Windows下,Cygwin 1.7或更高版本

將awk升級到最新版本(我們使用的是20070501)

GNU Make 3.81或更高版本(我們使用的是3.81)

如果其中任何一個的版本太舊,請在繼續之前先升級。

第1步:安裝NDK

既然NDK已經下載完成(沒錯吧?),你就需要解壓縮它。解壓後將它放入合適的目錄中。我們把它放在和Android SDK相同的目錄下。記住你把它放在哪裡了。

現在,你也許想要在路徑設定中新增NDK工具。如果你在Mac或Linux下,你可以用你的原生路徑設定來完成。如果你在Windows下的Cygwin,你就需要設定Cygwin的路徑設定。

第2步:建立專案

建立一個常規的Android專案。為了避免日後的問題,你的專案的路徑必須不包含空格。我們的專案有個叫做“com.mamlambo.sample.ndk1”的包,帶有一個叫做“AndroidNDK1SampleActivity”的預設Activity——你之後還會看到它們。

在這個專案的頂層建立一個叫做“jni”的目錄——這是你放置原生程式碼的地方。如果你很熟悉JNI,那你就會知道Android NDK很大程度上基於JNI的概念——它本質上是個只有有限的C語言編譯標頭檔案的JNI。

第3步:新增一些C語言程式碼

現在,在jni資料夾中,建立一個叫做native.c的檔案。一開始將以下C語言程式碼寫入該檔案,我們以後再新增另一個函式:

它獲取一個Java物件的字串引數,將它轉換為C-string,然後將它寫入到LogCat中。

不過該函式的名字很重要。它遵循了以“Java”的特定字樣開頭,後面跟著包名稱,然後類名稱,然後方法名稱,和Java中定義的一樣。每一部分都由一根下劃線隔開,而不是點。

該函式的頭兩個引數也很重要。第一個引數是JNI環境,它與helper函式會被頻繁呼叫。第二個引數是該函式所屬的Java物件。

第4步:從Java中呼叫原生程式碼

既然你已經寫好了原生程式碼,讓我們回頭看看Java這邊。在預設的Activity中,按照你的喜好建立一個按鈕,並新增一個按鈕處理器。從按鈕處理器中,對helloLog作呼叫:

23.helloLog("This will log to LogCat via the native call.");

然後你必須在Java這邊新增函式宣告。在你的Activity類中新增如下宣告:

24.private native void helloLog(String logThis);

它告訴編譯和連結系統該方法將在原生程式碼中實現。

最後,你需要載入原生程式碼最終編譯到的庫。在Activity類中新增如下的靜態初始化程式來根據名稱載入庫(庫的名字隨你決定,在下一步還會用到):

25.

26.

27.static {

28.

29.System.loadLibrary("ndk1");

30.

31.}

32.

第5步:新增原生程式碼的Make檔案

在jni資料夾中,現在你需要新增在編譯中要用到的makefile。該檔案必須以“Android.mk”命名,如果你之前命名的檔案為native.c,庫為ndk1,那麼Android.mk的內容就應該是這樣:

33.

34.

35.LOCAL_PATH:= $(call my-dir)

36.

37.

38.

39.include $(CLEAR_VARS)

40.

41.

42.

43.LOCAL_LDLIBS:= -llog

44.

45.

46.

47.LOCAL_MODULE:=ndk1

48.

49.LOCAL_SRC_FILES:=native.c

50.

51.

52.

53.include $(BUILD_SHARED_LIBRARY)

54.

第6步:編譯原生程式碼

既然你的原生程式碼已完成,make檔案也已就緒,是時候編譯原生程式碼了。在命令列下(Windows使用者在Cygwin下),你需要在你的專案的根目錄下執行ndk-build命令。ndk-build工具就在NDK工具目錄中。將它新增到我們的路徑中是最方便的辦法。

在之後的編譯中,如果你使用“ndk-build clean”命令,那麼你可以確保所有的東西都被重新編譯了。

第7步:執行程式碼

現在你已準備妥當可以執行程式碼了。在你最喜歡的模擬器或者手持裝置中載入該專案,檢視LogCat,然後點選按鈕。

可能有兩件事情會發生。首先,它可能正常工作了。這樣的話,恭喜你!不過你可能還是想要繼續閱讀下去。你也可能在LogCat中得到類似“Could not execute method of activity”這樣的錯誤。這很正常。這只是說明你漏掉了某個步驟罷了。用Eclipse很容易發生這種情況。通常,Eclipse被設定為自動重編譯。如果它不知道有東西被修改了,它就不會自動重編譯和重連結。在本例中,Eclipse不知道你編譯了原生程式碼。所以,“清除(cleaning)”該專案(在Eclipse工具欄中點選專案(Project)->清除(Clean)),強制Eclipse重編譯。

第8步:新增另一個原生函式

接下來的函式將不僅演示返回值的能力,還會演示返回例如字串這樣的物件的能力。在native.c中新增如下函式:

1.jstring Java_com_mamlambo_sample_ndk1_AndroidNDK1SampleActivity_getString(JNIEnv * env, jobject this, jint value1, jint value2)

2.{

3.char *szFormat = "The sum of the two numbers is: %i";

4.char *szResult;

5.// add the two values

6.jlong sum = value1+value2;

7.// malloc room for the resulting string

8.szResult = malloc(sizeof(szFormat) + 20);

9.// standard sprintf

10.sprintf(szResult, szFormat, sum);

11.// get an object string

12.jstring result = (*env)->NewStringUTF(env, szResult);

13.// cleanup

14.free(szResult);

15.return result;

16.}

17.

18.

為了正常編譯,你會需要新增一個include stdio.h的宣告。而且,為了響應這個新的原生函式,請在你的Activity Java類中新增如下宣告:

19.

20.private native String getString(int value1, int value2);

你現在可以隨意設定其功能。我們使用如下兩個呼叫和輸出:

21.String result = getString(5,2);

22.Log.v(DEBUG_TAG, "Result: "+result);

23.result = getString(105, 1232);

24.Log.v(DEBUG_TAG, "Result2: "+result);

回到C語言函式中,你會注意到我們做了許多事情。首先,我們在使用malloc()函式中的sprintf()呼叫時需要建立一個緩衝器(buffer)。如果你不會忘記通過使用free()函式清理結果,那麼這就很合理了。然後,為了傳回結果,你可以使用一個叫作NewStringUTF()的JNI helper函式。該函式基本上就是獲取一個C語言字串,以之建立一個新的Java物件。這個新的字串物件就可以在之後作為結果返回,你就可以在Java類中將它作為一個常規Java字串物件使用了。

指令集、相容性,等等

Android NDK需要Android SDK 1.5或更高版本。在新版本的NDK中,有些新的標頭檔案可用於擴大對某些API的訪問——特別是OpenGL ES庫。

不過,那些都不是我們要談論的相容性。這是原生程式碼,在使用時由處理器構架編譯。因此,你要問自己的一個問題會是它支援何種處理器構架?在目前的NDK中(在本文撰寫時)它只支援ARMv5TE和ARMv7-A指令集。預設設定下,目標架構被設定為ARMv5TE,它可以在使用ARM晶片的Android裝置上執行。

它預計未來將支援其他指令集(其中提到了x86)。這其中有很有意思的潛在狀況:NDK解決方案無法適用於所有的裝置。例如,市面上有使用x86指令集的英特爾(Intel)Atom處理器的Android平板裝置。

那麼NDK在模擬器上如何呢?模擬器執行的是真正的虛擬機器,包括完整的處理器虛擬。沒錯,這意味著在虛擬機器中執行Java就等於是在虛擬機器中運行了一個虛擬機器。