Android 開發手記一html
---- NDK 編程實例java
在 Android 上,應用程序的開發,大部分基於 Java 語言來實現。要使用 c 或是 c++ 的程序或庫,就須要使用 NDK來實現。 NDK 是 Native Development Kit 的簡稱。它是一個工具集,集成了 Android 的交叉編譯環境,並提供了一套比較方便的 Makefile ,能夠幫助開發者快速開發 C 或是 C++ 的動態庫,並自動的將 so 和 java 程序打包成 apk ,在Android 上運行。android
好,閒話少說,咱們以一個簡單的實例,來說解 NDK 的應用。c++
一 開發環境的搭建編程
這一步雖然沒什麼技術含量,可是對於初學者,有一個很好的入門指導,仍是頗有幫助的。windows
1.1 Android SDK 的搭建app
首先,要進行 Android 程序的開發, Android 的 SDK 是必需要安裝的。固然, Java 環境也必不可少。咱們先要安裝JDK 和 Eclipse ,這個能夠選比較新的版本,由於 Android 新的 SDK 已經不支持舊版本了。eclipse
1.1.1 JDK 能夠用 V5 或 V6 版本,下載地址 http://java.sun.com/javase/downloads/index.jspjsp
1.1.2 Eclipse 能夠用版本 version 3.4 or 3.5 ,下載地址 http://www.eclipse.org/downloads/ . 固然,若你須要其餘的Java 開發環境,能夠不用 Eclipse ,不過這樣也就用不了 ADT(Android Development Tools) 插件了。推薦仍是用 Eclipse來進行開發比較好,畢竟比較權威和方便麼。ide
1.1.3 安裝 SDK
Android SDK 下載地址爲 http://androidappdocs.appspot.com/sdk/tools-notes.html
1.1.4 爲 Eclips 安裝插件 ADT 。在 Eclipse 中,填加更新站點 https://dl-ssl.google.com/android/eclipse/
,
而後選擇安裝
ADT.
1.1.5 接下來,咱們選擇 Android 平臺和組件。如果在 window 系統下,運行 SDK Setup.exe ;如果在 Linux 系統下,運行 tools 目錄下的 android 程序,就能夠選擇須要的 Android Platform 和組件。
完成以上工做後,就能夠進行 Android 應用程序的開發了。能夠用 Eclipse 建立一個 Android 工程,比較簡單的Hello Android ,而後在模擬器下運行。具體的操做能夠參看 Android 開發網站的說明,上面有詳細的步驟。
1.2 Android NDK 的搭建
上面咱們搭建好了
SDK
的環境,能夠開發
Java
應用程序了。要開發
C
的程序,還得搭建
NDK
環境。
NDK
給咱們提供瞭如下內容:
libc (C library) headers
libm (math library) headers
JNI interface headers
bz (Zlib compression) headers
blog (Android logging) header
A Minimal set of headers for C++ support
1.2.1 NDK
的安裝
下載
NDK
安裝包,下載地址
http://androidappdocs.appspot.com/sdk/ndk/index.html
,下載後解壓便可使用。
1.2.2
若在
Linux
開發環境下那麼,這樣就可使用了。如果在
window
環境下,還須要安裝
cygwin
。
cygwin
下載地址:
http://www.cygwin.com/
這樣,
NDK
的環境也搭建好了。下面咱們來進行實戰演習。
二
NDK
開發實例
關於
NDK
的使用,首先須要瞭解一個概念:
JNI
。什麼是
JNI
?
2.1 Hello-jni
這個是
NDK
自帶的例子程序,安裝官方網站的說明,一步步來,應該沒有什麼問題,這裏就不細說了。
2.2 My God I did it
學習的第一步,就是模仿。咱們依照上面
Hello-jni
的例子,在建立本身的
NDK
程序。在此過程當中,對相關的內容和概念進行分析和說明。
首先,建立本身的
NDK
工程。咱們在
ndk
的
sample
目錄下建立本身的工程
myjni
,而後在這個文件夾子下,建立兩個目錄
jni
和
src
,
jni
用來放咱們的
c
文件,
src
是調用的
c
庫
java
接口文件。建立好目錄,接着建立文件
jni/myjni.c
,該文件比較簡單,就是輸出一個字符串,內容以下
|
這個程序,惟一和
hello-jni
不一樣的就是引用了
<android/log.h>
這個頭文件。在該頭文件中,聲明瞭函數
__android_log_print(),
能夠根據不一樣的
log
級別,輸出
log
,方便代碼的調試。在
NDK
中,
printf()
無法輸出,因此咱們須要藉助
log
庫來將咱們
c
代碼庫中須要輸出的內容,經過
java
控制檯輸出。調用函數
__android_log_print(),
就能夠在
Eclipse
中,查看
LogCat
來查看相關的輸出信息了。
注意:
在
c
文件中,函數名這樣定義:
Java_com_jpf_myjni_MyJNI_stringFromJNI
,有什麼講究麼?這個是
JNI
的標準,定義須要按照以下格式:
Java _packagename _classname _methodname ,
例如:
Java
_com_jpf_myjni _MyJNI _stringFromJNI
接着建立文件
jni/Android.mk.
這個文件是咱們本地
c
代碼的
Makefile
。文件內容以下:
|
分別對上述
Makefile
的語句進行說明。
LOCAL_PATH := $(call my-dir)
這句用來指定編譯的路徑。經過調用宏
my-dir
,獲取到當前工做的路徑。
include $(CLEAR_VARS) CLEAR_VARS
這個變量是編譯系統提供的,用來指明一個
GNU makefile
文件,添加這句,主要的目的是清理全部的
LOCAL_XXX.
,好比
LOCAL_MODULE
,
LOCAL_LDLIBS
。在每一個新模塊的開始處,須要添加這句。
LOCAL_MODULE := myjni
這句定義了模塊名稱,未來編譯的庫就以此命名。若果編譯的是動態庫,那麼庫名就是
libmyjni.so.
須要注意的是,若是你定義
module
爲
libmyjni
,那麼系統在生成動態庫的時候,就不要再爲你添加
lib
的前綴了,生成德動態庫名字仍是
libmyjni.so.
LOCAL_LDLIBS += -llog
這句指定了須要另外連接的庫。咱們在代碼中,用到了
log
庫,因此這裏加上這句。
include $(BUILD_SHARED_LIBRARY)
這句說明未來生產的庫是共享庫,及動態連接庫。若須要生產靜態庫,能夠這樣寫:
include $(BUILD_STATIC_LIBRARY)
。
寫完了
c
文件和
Makefile
文件,是否能夠編譯了呢?咱們試一下。在
cygwin
中,進入工程目錄,運行
ndk-build
,獲得下面的結果:
|
看到這個錯誤的意思是,缺乏
manifest
文件。老版本的
NDk
,工程中有一個
apps
,裏面包含了應用的程序文件和
Application.mk
。如今的版本,不須要咱們本身編寫
Application.mk,
,不過仍須要工程相關的配置信息。那麼如何作到呢?須要手工去寫
manifest
文件麼?不須要。咱們只須要在
Eclipse
中,建立工程就能夠了,這些配置文件會自動生成。
前面講過,在工程的
src
夾子下用來放置
Java
文件。咱們打開
Eclipse
,而後新建一個
Android
工程,工程名就叫
MyJNI
,工程路徑選擇咱們建立的
NDK
的路徑。這裏須要注意的是,工程名,包名等,須要和上面的
c
文件中的保持一致。
(Java
_com_jpf_myjni _MyJNI _stringFromJNI)
工程創建好後,編輯
src/com/jpf/myjni/MyJNI.java
文件,內容以下:
package com.jpf.myjni;
import android.app.Activity; import android.widget.TextView; import android.os.Bundle;
public class MyJNI extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); TextView tv = new TextView( this ); tv.setText( stringFromJNI() ); System. out .println( "Here we go ..." ); setContentView(tv); System. out .println( "Done!" ); }
public native String stringFromJNI(); static { System.loadLibrary ( "myjni" ); } } |
須要說明的幾點:
public native String stringFromJNI(); 這句申明,帶有 native 關鍵字,說明該方法是本地方法。
System.loadLibrary ( "myjni" ); 這句就是用來加載咱們的 c 動態庫的。上面聲明的方法,具體實現,就在咱們加載的庫中。
創建好工程,再次編譯,在
cygwin
中運行
ndk-build
,結果
OK
。
|
咱們看到,須要的共享庫已經生成,而且安裝好了。下面就能夠生成
apk
了。
在
Cygwin
中進行工程的
build
,編譯後,在工程的
bin
目錄下,會看到咱們的
apk
包。
好,咱們試試看,可否正常運行。在
Eclipse
選擇執行方式爲
Android Application
,點擊
run
,如下
console
的輸出:
|
上面的 warning ,是咱們沒有指定 API 的版本號。以下指定一下就沒有這個 warning 了。
下圖爲執行的效果:
下圖是咱們查看 LogCat 的輸出:
能夠看到咱們的輸出 MYJNI : MyJNI is called !
2.3 Study Hard
有了上面的基礎,咱們就能夠用
NDK
來進行項目開發了。
咱們常常會遇到這樣的問題,就是將一些現有的,成熟的
C
庫移植到
Android
平臺上。經過上面咱們的介紹,咱們已經知道,咱們須要用
JNI
來對現有的
C
庫包裝一下,而後提供
Java
接口,供上層調用。
首先的問題,就是
C
庫的編譯和測試。其實
Android
底層用的是
Linux
的內核,因此,和其餘
Linux
程序開發同樣,沒法使進行交叉編譯。不過,
Android
有些特殊的地方,咱們須要注意。下面就以一個很簡單的例子,講講如何應用
NDK
,作一個
C
的應用終端測試程序。
首先,建立
study-hadr/study-hard.c
文件,程序很是簡單,就是
Hello World
的
c
程序。
|
別看程序很簡單,不過這個程序的編譯可不簡單。
如果在
Linux
下,只須要執行:
gcc –o study-hard study-hard.c
就能夠生成應用程序
study-hard
了。
在
Android
下就不是這麼簡單了。在
Window
環境開發環境下,用到的交叉工具鏈,目錄是
/android-ndk-r4/build/prebuilt/windows/arm-eabi-4.4.0
。
在這個目錄的
bin
路徑下,你會看到
arm-eabi
爲前綴的諸多工具,這些就是
Android
用的編譯工具。那麼
c
庫和
c
頭文件又在哪裏呢?對於
Android
,不一樣的
Platform
,有不一樣的庫和頭文件,須要咱們本身選擇。好比,如今咱們要用
Platform5
,那麼
C
頭文件的路徑爲:
/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include
C
庫的路徑爲:
/android-ndk-r4/build/platforms/android-5/arch-arm/usr/lib
好了,咱們知道了
C
的編譯工具鏈,知道了
C
庫路徑和
C
頭文件路徑,應該能夠編譯了。寫個簡單的
Makefile
,試一下,結果出錯了。
crt0.o
沒有找到。
這個錯誤很糟糕,指出在連接的時候,找不到 crt0.o 。咱們在 Makefile 中添加以下幾句:
LDFLAGS += -nostdlib
-nostdlib 表示不鏈接系統標準啓動文件和標準庫文件 . 只把指定的文件傳遞給鏈接器。
此時編譯,結果爲:
錯誤指出,在連接的時候,找不到 puts ,這個函數是 c 庫中的,咱們添加以下語句再次嘗試:
LDFLAGS += -lc
咱們修改連接選項,增長對 dl 庫的連接, 再次嘗試:
LDFLAGS += -lc –ldl
此次生成了可執行文件,不過仍是有 warning ,在生成的可執行文件中,沒有找到入口 _start 。這個問題也比較奇怪。咱們查看下生成的可執行文件 :
readelf –a study-hard
發現生成的可執行文件,真的沒有入口函數。這是爲何呢?
在 Linux 下,用 -v 選項跟蹤下 gcc 編譯 hello world 程序的過程。會發現,在連接的過程當中,除了 hello.o, 還會連接crt1.o, crtn.o 等文件,正是這些文件,在生成可執行程序的過程當中,組成了 elf 文件中程序入口和程序退出等相關的處理部分。
查看咱們指定的 C 庫:
會發現, C 庫下有 crt 打頭的三個 .o 文件。咱們修改 Makefile ,連接 crtbegin 和 crtend 文件:
EXTRA_OBJS := $(PATH_PREFIX)/lib/crtbegin_dynamic.o $(PATH_PREFIX)/lib/crtend_android.o … … $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) |
再次編譯,結果以下,這次終於編譯成功了。
咱們將編譯好的程序放到 Android 上運行下看看效果。
顯示程序沒有找到。怎麼回事呢?繼續研究下 AndroidNDK 相關文檔。咱們還須要修改 Makefile 的一個地方:
LDFALGS += -Bdynamic -Wl,-dynamic-linker,/system/bin/linker
指定連接動態庫,動態鏈接器爲 /system/bin/linker
編譯後,再次運行,終於看到了 「Study hard ! 」