Android編譯篇


        Android的編譯系統涉及面極廣,包含編譯工具、印像文件編譯、SDK編譯、NDK編譯、目標系統配置等多個方面。盡管這些方面的內容煩瑣而晦澀,能夠參考的資料不多,可是系統設計尤其是系統架構人員必須熟悉它們。

1.源碼編譯

        基於源碼的方式進行開發,一般會依據目標環境的不同,對系統配置進行調整,如採用不同的引導器、特定的驅動、不同的文件系統、特定的屬性配置等,這就要求開發人員必須熟練掌握源碼的編譯方法和配置。

        (1)映像文件

                在編譯完源碼后。須要將生成的文件等打包成對應的文件系統。然后燒寫到移動終端。

在Android中,默認的文件系統為YAFFS2。當然OEM廠商能夠依據自己的須要,選擇其它的文件系統。

                在源碼中。YAFFS2文件系統的實現位於external\yaffs2\yaffs2文件夾下。它針對大容量的NAND flash進行了優化。具有掛載時間短等長處。在當前的智能終端中,還有一個比較經常使用的文件系統是UBIFS,它是由IBM和Nokia公司的project師於2006年開發的一款高效的嵌入式文件系統。

                在external\yaffs2\yaffs2\utils文件夾下包括了mkyaffsimage和mkyaffs2image兩種工具,前者用於生成YAFFS格式的映像文件。后者用於生成YAFFS2格式的映像文件。

                生成YAFFS2格式的映像文件的方法例如以下:

                        #mkyaffs2image dir imagename

                在啟動系統時,通過init.rc腳本會將文件系統掛載到特定的文件夾,方法例如以下:

                        #mount mtd partitions        //分區格式為MTD

                        #Mount /system rw first to give the filesystem a chance to save a checkpoint

                        mount yaffs2 mtd@system /system

                        mount yaffs2 mtd@system /system ro remount

                        mount yaffs2 mtd@userdata /data no suid nodev

                        mount yaffs2 mtd@cache /cache no suid nodev

                須要說明的是,文件系統涉及的概念包含掛載點、映像文件、MTD分區名等。

Android系統基本的掛載點\system、\data、\cache、\sdcard等,映像格式默認支持yaffs2、ext4、vfat等。

                在Android中,映像文件包含boot.img、ramdisk.img、system,img、userdata.img等。

依據硬件平台的不同。還有其它的映像文件。

映像文件的生成配置位於build\core\Makefile中。開發人員能夠依據自己的須要,配置映像文件。在build\core\Makefile中,除了定義映像文件外,還涉及源碼和SDK編譯的配置等內容。

                在編譯完畢后,自然要啟動模擬器,假設是商業開發。那么可能須要自己定義模擬器。在Linux編譯環境中顯式啟動一個模擬器。須要創建一個腳本。

以下是一個演示樣例:

                        #!/bin/sh

                        ANDROID_HOME=.

                        ANDROID_EMULATOR=$ANDROID_HOME/out/host/linux-x86/bin/emulator

                        ANDROID_SYSTEM=$ANDROID_HOME/out/target/product/generic/

                        ANDROID_KERNEL=$ANDROID_HOME/prebuilt/android-arm/kernel/kernel-qemu

                        ANDROID_SKIN=$ANDROID_HOME/sdk/emulator/skins

                        $ANDROID_EMULATOR -kernel $ANDROID_KERNEL -sysdir $ANDROID_SYSTEM -show-kernel -shell -data $ANDROID_SYSTEM/userdata.img \

                                -partition-size 128 -skindir $ANDROID_SKIN -skin CUSTOM1000 \   //自己定義模擬器

                                -sdcard $ANDROID_SYSTEM/sdcard.img -wipe-data

        (2)編譯方法

                Android中的編譯很easy。除了主要的全系統編譯外,Android還提供了幾種快捷方式供編譯和查找使用。

                1)編譯環境

                        Android的版本號眾多。隨着時間的遷移,其編譯環境也發生了一些變化,最大的變化是在Froyo版本號后,Android對Java的要求從Java 5提升到Java 6。同一時候對駐留的操作系統的要求從32位升級到64位。另外要求具備的Python2.4、Git 1.5.4或更高的版本號。

                        眼下。Android要求源碼的編譯在Linux或Mac OS下進行,推薦的操作系統為Ubuntu 10.04LTS(64位)或更高版本號。

以下以Ubuntu 10.04LTS為例介紹編譯環境的搭建。

                        (1)獲取Java

                                對於Gingerbread或更高的版本號,Android要求支持Java 6,採用Sun Java 6或者Open JDK 6均可。以下是安裝sun-java6-jdk的過程:

                                        #add-apt-repository "deb http://archive.canonical.com/ lucid partner"

                                        #add-apt-repository "deb-src http://archive.canonical.com/ubuntu lucid partner"

                                        #apt-get update

                                        #apt-get install sun-java6-jdk

                                        #update-java-allternatives -s java-6-sun

                                對於Froyo及更低的版本號,Android要求支持Java 5,這主要與當時Android無法兼容Java 6的overide屬性有關。以下是安裝sun-java5-jdk的安裝過程:

                                        #add-apt-repository "deb http://archive.ubuntu.com/ubuntu dapper main multiverse"

                                        #add-apt-repository "deb http://archive.ubuntu.com/ubuntu dapper-updates main multiverse"

                                        #apt-get update

                                        #apt-get install sun-java5-jdk

                                        #update-java-allternatives -s java-1.5.0-sun

                        (2)基本開發包

                                另外,對於64位的Ubuntu 10.04 LTS,還須要進行例如以下的安裝:

                                        #apt-get install git-core gnupg flex bison gperf build-essential zip curl zliblg-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncureses5-dev is32-libs xllproto-core-dev libxll-dev lib32readline5-dev lib32z-dev

                                對於32位的Ubuntu 10.04 LTS,則須要進行例如以下安裝:

                                        #apt-get install git-core gnupg flex bison gperf build-essential zip curl zliblg-dev gcc-multilib g++-multilib libc6-dev-i386 ncurses5-dev xllproto-core-dev libxll-dev readline5-dev

                                假設希望進行原生代碼的內存泄露方面的檢測,那么須要安裝Valgrind包。

                        (3)安裝repo

                                為了獲取Android的源碼。必須安裝repo。

repo是Google對git的封裝,使對git的操作更加方便。

                                以下是安裝repo的方法:

                                        #medir ~/bin

                                        #PATH=-/bin:$PATH

                                        #curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo>~/bin/repo

                                        #chmod a+x ~/bin/repo

                                 注意:repo也是不斷升級的,並對駐留的操作系統的配置有依賴性,比方對Python就有要求,偶爾會發生更新后無法操作repo的情況。

                2)源碼獲取

                        獲得源碼的方式很easy,依據筆者的經驗。建議創建兩個目錄。分別用於獲取開發版本號和最新版本號。假設開發的目標環境是Froyo,則創建froyo和android兩個目錄。當中froyo目錄用於獲取froyo分支,而android目錄用於獲取主分支(master)。

                        獲取主分支的方法例如以下:

                                #repo init -u https://android.googlesoutce.com/platform/manifest

                        獲取特定分支的方法例如以下:

                                #repo init -u https://android.googlesource.com/platform/manifest -b froyo

                        在初始化完畢過程中,須要設置開發人員的名字、郵箱地址,以及一些狀態顯示,通常建議使用Gmail郵箱。在初始化完畢后,就可以下載源碼。下載源碼的方法例如以下:

                                #repo sync

                        注意:repo不支持端點續傳,repo的下載是基於單個project進行的,在Android中。眼下包括了170個左右的project,其列表位於.repo\project.list中。處於開發階段的project和內核project不會被下載。在下載完畢前,源碼會被隱藏,直到下載完畢后。開發人員才干看到下載的源碼。

                        假設基於源碼的方式進行開發,內核的代碼是必須的,通常能夠從硬件廠商哪里獲得最新的代碼。

當前Android源碼樹中已經包括了Qualcomm、TI、Qernu(模擬器)、Sansung、NVIDIA等廠商的代碼。

                        對於普通開發人員而言,不須要向Android倉庫提交代碼,且其對repo的使用方法要求較低。假設希望了解repo的很多其它信息。請參考http://soutce.android.com

                3)基本編譯過程

                        Android的基本編譯過程主要包含3部分:環境變量設置、設置編譯屬性配置、運行編譯。首先通過終端切換到Android的根文件夾下,然后運行例如以下操作:

                                #.build/envsetup.sh

                                #lunch 1        //設置目標環境,lunch菜單選項為full -eng

                        上述代碼中,lunch的語法規則為:lunch<product><variant>。眼下Android通過LUNCH_MENU_CHOICES數組提供了4種默認的lunch菜單選項(simulator僅存在於駐留操作系統為Linux的環境中),例如以下所看到的:

                                 LUNCH_MENU_CHOICES[4]={full-eng, full_x86-eng, vbox_x86-eng, simulator}

                        為了運行full-eng目標環境編譯,需聲明lunch 1;為了運行full_x86-eng目標環境編譯。需聲明lunch 2;對於其它lunch菜單選項。則需顯示聲明,如為了編譯SDK,其方法為lunch sdk-eng。

                        另外,Android還支持多CPU的編譯,比如運行make -j4,意味着編譯工作能夠同一時候在最多4個CPU上同一時候進行,這在CPU普遍為多核架構的今天。是個不錯的設計。遺憾的是,Android沒有公布官方的基於分布式編譯的工具,無法有效地利用局域網內部的空暇計算能力。

                        對於一台高性能的PC而言,完畢整個編譯過程需1.5h左右,之后會在out文件夾下發現3個文件夾:host、target、tmp。當中host文件夾放置的是工具信息,target文件夾放置的是幫助文檔,中間生成文件、輸出的文件系統和映像文件等,tmp文件夾放置的是Apache的harmony的一些測試信息。比如,out\target\product\generic\installed-files.txt記錄了輸出的文件系統的信息。

                4)快捷方式

                        眼下Android支持的快捷方式包含croot、m、mm、mmm、cgrep、jgrep、resgrep、godir等。

                        croot:用於改變當前路徑到Android根文件夾。

                        m:用於從Android根文件夾開始編譯。

                        mm:用於編譯當前文件夾下的全部模塊。

                        mmm:用於編譯特定文件夾下的全部模塊。

                        cgrep:用於在C/C++文件里查找。

                        jgrep:用於在Java文件里查找。

                        resgrep:用於在資源文件里查找。

                        godir:用於跳轉到某個文件夾。

        (3)主要腳本

                Android中的腳本類文件主要用來配置產品、目標板。以及依據開發人員的Host和Target來選擇對應的工具並設定對應的編譯選項。

編譯系統的主要腳本包含envsetup.sh、config.mk、envsetup.mk、product_config.mk、BoardConfig.mk、version_defaults.mk、product.mk、build_id.mk、AndroidProducts.mk、Makefile等。Android運行編譯所涉及的主要腳本之間的調用關系例如以下圖所看到的:

                        

                AndroidProducts.mk包括了詳細的應用配置腳本,如在Passion目標環境中,HTC引用的是full_passion.mk腳本;product_config.mk主要定義了AAPT、產品制造商、WIFI、OTA等相關信息:product.mk定義可產品的一些變量信息。

                對模塊編譯進行控制,主要是通過core.mk、generic.mk、sdk.mk等腳本及特定目標環境的腳本進行的;對於單個模塊進行控制。主要是通過Android.mk和CleanSpec.mk等腳本進行的。以下對主要腳本進行具體的介紹:

                1)envsetup.sh

                        envsetup.sh腳本的主要功能包含定義環境變量信息、載入系統配置信息(軟件信息、硬件配置)、定義編譯快捷方式(如mm、mmm等)、調試、冒煙測試、GDB調試等。

                        若編譯代碼,就要涉及代碼的編譯工具。眼下Android支持的原生代碼編譯工具鏈位於prebuild文件夾下,包含交叉編譯工具鏈和普通編譯工具鏈。眼下交叉編譯工具鏈為arm-eabi。

其當前版本號為4.4.3。所謂EABI。即應用程序二進制接口(Embedded Application Binary Interface)。又稱為GUN EABI。EABI和早期的OABI(old ABI)均是針對ARM架構的CPU的,EABI支持軟件浮點和硬件實現浮點功能混用,其系統調用的效率更高。兼容性更好,對軟件浮點的支持效率比OABI高非常多。

                        普通編譯工具鏈包含i686-linux-glibc2.7-4.4.3、i686-unknown-linux-gnu-4.2.1和sh-4.3.3等。設置交叉編譯工具鏈的步驟例如以下:

                                export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.4.3/bin

                                export ANDROID_TOOLCHAIN=$ANDROID_EABI_TOOLCHAIN

                                export ANDROID_QTOOLS=$T/development/emulator/qtools

                        設置java編譯工具的方法例如以下:

                                function set_java_home(){

                                        if[!"$JAVA_HOME"]; then

                                                case 'uname -s' in

                                                        Darwin(export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home;;)

                                                        export JAVA_HOME=/usr/lib/jvm/java-6-sun;;

                                               esac

                                       fi

                               }

                        眼下Android的Makefile文件名稱為Android.mk,與標准的Makefile同樣。

                2)config.mk

                        config.mk用於定義系統相關的配置信息和編譯變量等,是Android編譯系統中很重要的一個腳本。以下是config.mk中對輸出包后綴的設置:

                                COMMON_PACKAGE_SUFFIX:=.zip

                                COMMON_JAVA_PACKAGE_SUFFIX:=.jar

                                COMMON_ANDROID_PACKAGE_SUFFIX:=.apk

                        以下是config.mk中載入目標環境的過程:

                                board_config_mk:=$(strip $(wildcard$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk\

                                device/*/$(TARGET_DEVICE)/BoardConfig.mk vendor/*/$(TARGET_DEVICE)/BoardConfig.mk)) 

                        在默認情況下,目標環境信息位於SRC_TARGET_DIR、device、vendor下。

開發人員能夠將自己定義的目標環境文件夾放置在這些文件夾下。

                        比較重要的編譯變量包含CLEAR_VARS、BUILD_STATIC_LIBRARY、BUILD_SHARED_LIBRARY和BUILD_PACKAGE等。這些變量在構建應用的android.mk腳本中會用到。開發人員必須掌握其含義。

                3)envsetup.mk

                        envsetup.mk主要用於推斷駐留的操作系統環境下、環境變量設置。比較重要的環境變量包含TARGET_PRODUCT、TARGET_BUILD_VARIANT、HOST_OS、BUILD_OS、BUILD_ARCH、HOST_BUILD_TYPE、OUT_DIR等。

                        (1)TARGET_PRODUCT表示編譯的目標環境

                                 TARGET_PRODCT的定義詳細有OEM廠商決定。相應於特定的目標環境,須要在build\target或device文件夾下存在特定的目標環境配置文件夾,比方相應generic目標環境,在build\target\board\generic下定義了generic目標環境的詳細配置(如系統屬性、鍵盤配置等)。而對於Samsung的crespo目標環境,配置文件文件夾為device\Samsung\crespo。

                        (2)TARGET_BUILD_VARIANT表示目標編譯變量

                                說明有哪些文件被納入編譯控制。眼下TARGET_BUILD_VARIANT的值包含eng、user、debug、tests等。

                        (3)HOST_OS用於設置駐留的操作系統

                                眼下Android僅支持在Linux或Mac下編譯源碼,而在Windows下需借組於Linux仿真環境Cygwin才干編譯源碼。

眼下Android支持的HOST_OS的值包含linux、Darwin(用於Darwin、Mac等操作系統)、windows等。推斷操作系統的方法例如以下:

                                        UNAME:=$(shell uname-sm)        //UNAME包括了操作系統和CPU架構的信息

                                        ifneq(,$(findstring Linux,$(UNAME)))

                                        HOST_OS := linux

                                        endif

                        (4)BUILD_OS表示正真運行編譯的操作系統,眼下BUILD_OS與HOST_OS等同。

                        (5)BUILD_ARCH表示主流處理器架構,眼下Android僅支持x86和PPC兩種架構。

                        (6)HOST_BUILD_TYPE表示編譯的類型,眼下Android支持release和debug兩種類型。debug用於調試。

                        (7)OUT_DIR表示輸出文件的路徑

                                 眼下輸出文件位於out文件夾下,主要由target、host和common 3部分構成。

以下是定義OUT_DIR的實現:

                                         ifeq(,$(strip $(OUT_DIR)))

                                         OUT_DIR := $(TOPDIR)out

                                         endif

                4)BroadConfig.mk

                        BroadConfig.mk用於設置硬件相關的信息。是構建目標環境配置的重要腳本。眼下Android定義的目標環境包含Emulator、Generic、Generic_x86、Sim。除了以上目標環境外,Android源碼還包含了HTC的passion與dream,以及Samsung的crespo等目標環境,能夠作為驅動開發者進行目標環境配置的參考。

                        build\target\board\generic\文件夾下BoardConfig.mk的實現例如以下。當中定義了引導器、內核、編譯器、驅動、分區配置等方面的信息。

                                TARGET_NO_BOOTLOADER:=true

                                TARGET_NO_KERNEL:=true

                                TARGET_CPU_ABI:=armeabi

                                HAVE_HTC_AUDIO_DRIVER:=true

                                BOARD_USES_GENERIC_AUDIO:=true

                                TARGET_SHELL:=mksh

                        crespo目標環境在BoardConfig.mk中對分區配置的定義例如以下:

                                BOARD_NAND_PAGE_SIZE:=4096 -s 128

                                BOARD_KERNEL_BASE:=0x30000000

                                BOARD_KERNEL_PAGESIZE:=4096

                                BOARD_KERNEL_CMDLINE:=console=ttyFIQ0 no_console_suspend

                                TARGET_RECOVERY_UI_LIB:=librecovery_ui_crespo

                                TARGET_RELEASETOOLS_EXTENDIONS:=device/samsung/crespo

                                TARGET_USERIMAGES_USE_EXT4:=true

                                BOARD_SYSTEMIMAGE_PARTITION_SIZE:=536870912

                                BOARD_USERDATAIMAGE_PARTITION_SIZE:=1073741824

                                BOARD_FLASH_BLOCK_SIZE:=4096

                        在目標環境中。還有一個比較重要的腳本是AndroidBoard.mk。通經常使用於定義按鍵布局等信息,與鍵盤有關的信息定義在tuttle2.ki和tuttle2.kcm等文件里。

build\target\board\generic\文件夾下AndroidBoard.mk的實現例如以下:

                                LOCAL_PATH:=$(call my-dir)

                                file:=$(TARGET_OUT_KEYLAYOUT)/tuttle2.k1

                                ALL_PREBUILT+=$(file)

                                $(file):$(LOCAL_PATH)/tuttle2.k1|$(ACP)

                                $(transform-prebuilt-to-target)

                                include $(CLEAR_VARS)

                                LOCAL_SRC_FILES :=tuttle2.kcm

                                include $(BUILD_KEY_CHAR_MAP)

                        當然在實際實現中。關於目標環境的配置比較復雜,開發人員須要依據自身的實際情況靈活配置。

                5)version_defaults.mk

                        version_defaults.mk用於處理各種編譯版本號信息,如PLATFORM_VERSION、PLATFORM_SDK_VERSION、BUILD_ID、BUILD_NUMBER等。

                        對於主分支的源碼,其PLATFORM_VERSION的值為AOSP。PLATFORM_SDK_VERSION的值為數字,BUILD_ID的值為OPENMASTER、BUILD_NUMBER的值是依據編譯日期生成的。

                        對於Gingerbread分支的代碼,其PLATFORM_VERSION當前的值為2.3.7。開發人員假設希望獲取當前執行環境的版本號信息,那么能夠通過build.java來實現。方法例如以下:

                                Build.DISPLAY        //BUILD_ID,如“OPENMASTER”

                                Build.VERSION.RELEASE      //公布版本號,如“3.4b5”

                                Build.VERSION.SDK_INT       //SDK版本號,如“8”

                6)build_id.mk

                        build_id.mk用於定義版本號分支信息。其應用方法例如以下:

                                BUILD_ID:=OPENMASTER       //編譯分支

                                DISPLAY_BUILD_NUMBER:=true        //是否顯示版本

                7)Makefile

                        除以上腳本外。還有一個很重要的腳本為build\core\Makefike,它用於定義生成各種映像文件的配置,包括boot.img、ramdisk.ing、userdata.ing、syetem.img、recovery.img等。以下是定義system.img包括內容的過程:

                                systemimage_intermediates:=$(call intermediates-dir-for, PACKAGING, systemimage)

                                BUILT_SYSTEMIMAGE:=$(systemimage_intermdiates)/system.img

                                INTERNAL_SYSTEMIMAGE_FILES:=$(filter $(TARGET_OUT)/%, $(ALL_PREBUILT) $(ALL_COPIED_HEADERS) $(ALL_GENERATED_SOURCES)

                                                                                             $(ALL_DEFAULT_INSTALLED_MODULES))

                        以下是生成system.img的過程:

                                define build-systemimage-target

                                @echo "Target system fs image: $(1)"

                                @mkdir -p $(dir $(1))

                                $(hide) $(MKYAFFS2)  -f $(mkyaffs2_extra_flags) $(TARGET_OUT) $(1)

                                endef

                                endif #INTERNAL_USERIMAGES_USE_EXT

                                $(BUILT_SYSTEMIMAGE): $(INTENAL_SYSTEMIMAGE_FILES)

                                $(INTENAL_USERIMAGES_DEPS)

                                $(call build-systemimage-target, $@)

                                INSTALLED_SYSTEMIMAGE:=$(PRODUCT_OUT)/system.img

                                SYSTEMIMAGE_SOURCE_DIR:=$(TARGET_OUT)

                        其它映像文件的生成方法和system.img類似。

                8)core.mk

                        core.mk中定義了產品的一些基本信息和核心包。基本信息主要包含產品的BRAND、DEVICE及產品屬性(如提示音、振鈴音、多媒體框架配置等)。

                9)generic.mk

                        generic.mk定義了generic目標環境的應用編譯控制腳本,主要側重將哪些應用增加到編譯系統中,假設開發人員希望將實現的某一應用增加到編譯系統中。應在generic.mk中增加對應的配置是該應用的LOCAL_PACKAGE_NAME納入PRODUCT_PACKAGES變量的控制中。當然,針對不同的目標環境,對應的應用編譯控制腳本並不同樣。

                        比如,希望將Calendar應用增加編譯系統,通過查看package\apps\Calendar\文件夾下Android.mk中相關的配置。獲悉其LOCAL_PACKAGE_NAME為Calendar,將應用增加編譯系統的方法例如以下:

                                PRODUCT_PACKAGE:=  \

                                ...

                                Calender  \

                                ...

                10)sdk.mk

                        假設是在編譯SDK,則應注意sdk.mk腳本,其和generic.mk一樣具有編譯控制功能。

可是除了對普通應用進行控制外,依據SDK的特點。sdk.mk還將Android工具、資源相關的信息納入到編譯系統中。

                11)Android.mk

                        對於單個project,Android是通過Android.mk來進行編譯控制的。定義的信息一般包含LOCAL_SRC_FILES、LOCAL_PACKAGE_NAME等本地環境變量。一個簡單的HelloActivity的Android.mk實現例如以下:

                                LOCAL_PATH:=$(call my-dir)        //設置路徑為當前路徑

                                include $(CLEAR_VARS)        //清空LOCAL_SRC_FILES等設置環境變量

                                LOCAL_MODULE_TAGS:=samples       //設置模塊表示

                                LOCAL_SRC_RILES:=$(call all-java-files-under, src)        //設置源碼路徑為\src

                                LOCAL_PACKAGE_NAME:=HelloActivity        //設置包名

                                LOCAL_SDK_VERSION:=current        //設置SDK版本號

                                include $(BUILD_PACKAGE)        //調用編譯腳本

                                include $(call all-makefiles-under, $(LOCAL_PATH))        //引入當前路徑下的全部編譯腳本

                        清空本地環境變量實際上是調用build\core\文件夾下的Clear_vars.mk完畢的;LOCAL_MODULE_TAGS的可選值包含samples、optional、eng、debug、tests、cts、user等。其默認值為optional,對於普通應用,LOCAL_MODULE_TAGS的值為optional。LOCAL_SDK_VERSION的默認值為current,可是假設希望在Gingerbread源碼環境中編譯基於Froyo的應用,則應設置LOCAL_SDK_VERSION的值為8.調用編譯腳本實際上是調用了build\core\文件夾下的package.mk的內容。

                        假設應用代碼中包括AIDL文件,那么僵AIDL文件加入到源碼樹中的方法例如以下:

                                LOCAL_SRC_FILES:=$(call all-java-files-under, src)

                                LOCAL_SRC_FILES+=src/com/example/android/apis/app/IRemoteService.aidl   \

                                                                      src/com/example/android/apis/app/IRemoteServiceCallback.aidl  \

                                                                      src/com/example/android/apis/app/ISecondary.aidl  \

                        (1)載入共享庫

                                其實,android.mk還支持更復雜的配置。如載入Java庫和原生庫,同一時候運行多個編譯任務等。以下以Calculator為例介紹復雜的Android.mk實現。

Calculator的Android.mk運行了兩個編譯任務,除了編譯應用外。還載入了一個JAR庫。一個相關的示比例如以下:

                                         LOCAL_PATH:=$(call my-dir)

                                         include $(CLEAR_VARS)

                                         LOCAL_MODULE_TAGS:=optional

                                         LOCAL_STATIC_JAVA_LIBRARIES:=libarity        //載入靜態庫

                                         LOCAL_SRC_FILES:=$(call all-java-files-underm src)

                                         LOCAL_SDK_VERSION:=current

                                         LOCAL_PACKAGE_NAME:=Calculator

                                         include $(BUILD_PACKAGE)

                                         include $(CLEAR_VARS)        //編譯靜態庫

                                         LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=libarity:arity-2.1.2.jar

                                         include $(BUILD_MULTI_PREBUILT)        //引入預處理腳本

                                         include $(call all-makefiles-under, $(LOCAL_PATH))

                               為了載入JAR庫,須要重點了解LOCAL_STATIC_JAVA_LIBRARIES和LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES兩個本地環境變量。

前者表示應用載入的庫名,后者用於設置與庫名相應的詳細的JAR庫。假設希望載入多個JAR庫,那么能夠按下面方法設置本地環境變量:

                                         LOCAL_STATIC_JAVA_LIBRARIES:=lib1 lib2

                                         LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=lib1:1.jar lib2:2.jar

                               除了JAR庫外,部分應用可能還包括了基於JNI的原生代碼實現。這些原生代碼通常被編譯成共享庫的形式。如果生成的共享庫為libnative.so,那么在應用的Android.mk文件里載入共享庫的方法例如以下:

                                       LOCAL_JNI_SHARED_LIBRARIES:=libnative

                                假設是在SDK下進行開發,那么對JAR庫和原生共享庫均不須要進行配置,通常將JAR庫放置在應用的根文件夾或libs文件夾下,將原生共享庫放置在libs\armeabi文件夾下。

                        (2)應用權限

                                 要進行限制級的操作,如查看電話簿。撥打電話等,就涉及權限問題。

Android.mk支持設置本地證書,方法例如以下:

                                         LOCAL_CERTIFICATE:=platform

                                 眼下LOCAL_CERTIFICATE的可選值包含platform、shared、media、testkey、cts/tests/appsecurity-tests/certs/cts-testkey1、cts/tests/appsecurity/certs/cts-testkey2等。當中。platform便是系統證書。通常和在AndroidManifest.xml中增加的android:sharedUserId="android.uid.system"屬性結合使用。

                        (3)混淆器設置

                                  Java的解釋性編譯的特性,注定了在保護開發人員的知識產權方面會存在風險。在當前強大的反編譯技術下,假設不進行混淆。那么應用的Java代碼對全部人來說都差點兒是一目了然的。為了保護開發人員的知識產權和企業的商業機密,Android引入了proguard混淆器。

                                  在Android中載入groguard混淆器的方法例如以下:

                                          LOCAL_PROGUARD_FLAG_FILES:=proguard.flags

                                  以Launcher2為例。其proguard.flags實現例如以下:

                                          -keep class com.android.launcher2.Launcher{

                                                  public void previousScreen(android.view.View);

                                                  public void nextScreen(android.view.View);

                                          }

                                          -keep class com.android.launcher2.AllApps3D$Defines{

                                                  *;

                                          }

                                          -keep class com.android.launcher2.ClippedImageView{

                                                  *;

                                          }

                                假設不希望進行Java混淆,則能夠進行例如以下設置:

                                        LOCAL_PROGUARD_ENABLED:=disabled

                                假設是基於SDK進行開發,那么Eclipse會自己主動幫組應用進行混淆。

                        (4)安裝到特定文件夾下

                                在開發某些特定應用時。須要將特定的數據安裝到映像文件的特定文件夾下,如會考慮將配置文件安裝到etc文件夾下等。

                                以下是將android.software.live_wallpaper.xml安裝到\system\etc\permissions文件夾下的方法:

                                        include $(CLEAR_VARS)

                                        LOCAL_MODULE:=android.software.live_wallpaper.xml

                                        LOCAL_MODULE:=ETC

                                        LOCAL_MODULE_PATH:=$(TARGET_OUT_ETC)/permissions

                                        LOCAL_SRC_FILES:=$(LOCAL_MODULE)

                                LOCAL_MODULE_CALSS的可選值有EXECUTABLES、ETC、DATA、STATIC_LIBRARIES、JAVA_LIBRARIES、SHARED_LIBRARIES等。

                12)CleanSpec.mk

                        CleanSpec.mk用於在編譯時清除遺留的中間文件和數據文件,通常不須要進行設置。以下是CleanSpec.mk中的部分實現:

                                $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)

                                $(call add-clean-step, rm -rf (OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)

                                $(call add-clean-step, rm -rf find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)

                                $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)

        (4)環境變量

                在Android編譯系統中。有幾個重要的環境變量須要注意,以下進行簡單的介紹。

                1)ANDROID_TOOLCHAIN

                        ANDROID_TOOLCHAIN主要用於設置交叉編譯工具鏈,眼下Android的交叉編譯工具鏈為arm-eabi-4.4.3。須要注意。工具鏈的工具包含ar、as、c++、g++、gcc、ld、nm、objcopy、objdump、ranlib、strip等。查看模塊間依賴關系的工具ldd並不在當中。

                2)ANDROID_PRODUCT_OUT

                        ANDROID_PRODUCT_OUT定義了編譯目標環境輸出的絕對路徑。對於generic文件夾環境而言,其ANDROID_PRODUCT_OUT的值為ANDROIDROOT/out/target/product/generic,由TARGET_PRODUCT_OUT_ROOT和TARGET_DEVICE組成。

                3)TARGET_PRODUCT

                        TARGET_PRODUCT表示編譯的目標環境,這是Android編譯系統中最重要的環境變量。眼下Android提供了多個目標環境。包含sdk、sim、full、full_x86、generic、full_crespo等。另外。HTC、Samsung等廠商的部分機型的目標環境也進行了開源。

                        須要說明的是。generic表示最低配置;full表示集成全部語言、應用、輸入法的配置;full_crespo表示針對Nexus S的配置。

                4)TARGET_BUILD_VARIANT

                        TARGET_BUILD_VARIANT定義了編譯變量。眼下Android支持的編譯環境包含eng、user、debug、tests等。

                5)TARGET_BUILD_TYPE

                        TARGET_BUILD_TYPE表示編譯的類型。指定編譯的是debug還是release類型。debug表示包括調試信息。TARGET_BUILD_TYPE能夠幫組開發人員進行GDB調試。

                6)TARGET_SIMULATOR

                        TRAGET_SIMULATOR為一個布爾型的變量。用於推斷輸出目標環境的是真實的設備還是模擬器。

        (5)目標環境

                目標環境由TARGET_PRODUCT和TARGET_BUILD_VARIANT共同定義。眼下Android自帶的編譯模式有full-eng、full_x86-eng和simulator等。

                在進行源碼編譯時,通過例如以下方式能夠指定目標環境:#lunch "TARGET_PRODUCT" -"TARGET_BUILD_VARIAN",接着直接運行make就可以運行對應的目標環境編譯。

2.SDK編譯

        在OEM開發中,常常須要公布SDK供應用開發者使用。依據駐留操作系統的不同。Android的SDK編譯分為Linux和Windows兩種環境下的編譯。當然假設在開發過程中,對ADT相關的內容也做了改動。則還需編譯ADT插件。通常ADT插件採用Android官方公布的版本號就可以。

關於SDK的編譯,在sdk\docs\文件夾下的howto_build_SDK.txt文檔中有很具體的介紹。

        (1)Linux下的SDK編譯

                編譯SDK和編譯源碼的Android中沒有本質的不同,設置好目標環境就可以。編譯Linux下的SDK的步驟例如以下:

                        #.build/envsetup.sh

                        #lunch sdk-eng

                        #make sdk

                假設駐留的操作系統為基於x86架構的Linux,則輸出文件為out\host\linux-x86\sdk\android-sdk_eng.<build-id>-x86.zip。假設駐留的操作系統為基於x86構架的Mac OS,則輸出文件為out\host\darwin-x86\sdk\android-sdk_eng.<build-id>_mac-x86.zip。

        (2)Windows下的SDK編譯

                Windows下的SDK的生成依賴於Linux、Mac下的SDK。這意味着為了編譯Windows下的SDK。必須先編譯Linux或Mac下的SDK。生成Windows下的SDK意味着將Linux或Mac下的SDK中與操作系統相關的UNIX二進制文件替換為Windows下的二進制文件。

                在生成Linux或Mac下的SDK后,為了生成Windows下的SDK。需切換到XP或者Vista版本號的Windows操作系統下。另外還須要安裝Cygwin仿真環境。可是。Android尚不支持最新的Cygwin1.7,開發人員必須安裝Cygwin 1.5。

Cygwin 1.5的下載地址為http://cygwin.org/win-9x.html

                安裝Cygwin須要下載的軟件安裝包包含autoconf、bison、curl、flex、gcc、g++、git、gnupg、make、mingw-zlib、python、zip、unzip等;建議下載的軟件包包含diffutils、emacs、openssh、rsync、vim、wget等。不應下載readline軟件包。然后在Cygwin環境下下載android源碼。接着才干開始正真的Windows下的SDK編譯。

其步驟例如以下:

                        #mkdir ~/mysdk

                        #export SDK_NUMBER=$(USER)-'data+%Y%m%d-%H%M%S'

                        #development/build/tools/make_windows_sdk.sh /path/to/macos/or/linux/sdk.zip ~/mysdk

                運行完畢以上過程后就可以在mysdk文件夾下看到生成的Windows下的SDK了。

        (3)ADT插件的編譯

                為了編譯ADT插件,須要先從Eclipse的官方站點上下載Eclipse 3.4或以上的Java版本號。這里下載eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz。然后將其解壓到~/eclipse-3.4文件夾下,設置ECLIPSE_HOME例如以下:

                        #export ECLIPSE_HOME=~/eclipse-3.4/eclipse

                然后為Eclipse創建桌面的啟動器,將其作為開發應用的開發環境。接着就可以編譯ADT插件了。

                        #mkdir ~/mysdk

                        #development/tools/eclipse/scripts/build_server.sh ~/mysdk $USER

                稍等片刻就可以在~/mysdk文件夾下看到ADT插件android-eclipse-<buildnumber>.zip了。

3.NDK編譯

        因為性能的原因。非常多的開發人員希望可以利用C/C++等原生代碼在Android平台上開發應用,尤其是當基於OpenGl和OpenCore開發的游戲、信息安全和多媒體方面的應用會涉及一些性能敏感或復雜的算法時。另外,一些基於原生代碼開發的應用也有向Android移植的需求。假設是在源碼環境下開發,那么直接將原生代碼在源碼環境下進行組織就可以。

        Android NDK編譯的應用能夠在Android 1.5及以上版本號的Android上執行。對於本地調用涉及的JNI,須要注意的是JNI調用的開銷不小。

因此。簡單的操作不必採用JNI調用,且基於原生代碼開發的代碼還可能帶來安全性的問題。另外。NDK提供的原生方法僅僅是Android源碼能力的一個子集。

        眼下最新版的NDK為Android NDK r6b。在該版本號中。Android提供了對很多其它原生代碼能力(如C++異常處理、RTTI、STLport、FNU libstdc++等)的支持。

        在通常情況下,在Android中,原生代碼並非編寫界面代碼的最佳方式。

為了處理Android系統時間。避免ANR(Application Not Responding)情況的出現,界面代碼最后通過Java來編寫。原生代碼通常以動態共享庫的方式出如今Androidproject中。通過NDK編譯出來的動態共享庫的名字一般為libFileName.so,當中lib為動態共享庫的前綴。為了在Java代碼中載入動態共享庫,可運行例如以下操作:

                static{        System.loadLibrary(FileName);        }

        在Java代碼中引用原生代碼的方法例如以下:        native byte[] loadFile(String parm);

        有時希望在原生代碼章加入在Logcat中能夠看到的log信息。實現方法例如以下:

                __android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))

        上述代碼中,ANDROID_LOG_DEBUG為log登記,相關的log等級定義位於$NDK_ROOT/platforms/android-9/arch-arm/usr/include/android/log.g中。

        將NDK壓縮包解壓,會發現其根文件夾下有3個可運行腳本:ndk-build、ndk-gdb、GNUmakefile。

另外還會發現NDK主要由編譯腳本、文檔、部分源碼、例子、測試、工具鏈、平台共享庫等組成。

        NDK中有幾個重要的宏變量須要了解。

                NDK_ROOT:NDK的安裝根文件夾。

                LOCAL_CPP_EXTENSION:用於顯示定義C++文件的后綴,默認C++代碼的后綴為.cpp。

                TARGET_ARCH:用於指明CPU的架構。如arm指的是ARM兼容性架構。

                TARGET_ARCH_ABI:用於指明CPU+ABI的目標平台。若armeabi值的是Armv5TE,armeabi-v7a指的是基於ARM v7指令集的Cortex-A8等。

                TARGET_PLATFROM:用於指明Android版本號的目標平台。如希望目標環境為Froyo,則可將TARGET_PLATFORM的值設置為android-8。

另外Android NDK還提供了一個環境變量TARGET_ABI。其格式為$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)。如希望目標環境為基於Armv5TE的Froyo。可直接設置TARGET_ABI為android-8-armeabi。

        為了開發一個包括原生代碼的應用,如果project所在的文件夾有PROJECT宏定義。過程例如以下:

                將原生代碼放置在$(PROJECT\jni下,並編寫JNI接口代碼。

                編寫$(PROJECT\jni\Android.mk以描寫敘述編譯過程,如須要編譯的源文件、模塊名等。

在同一個Android.mk中能夠定義多個模塊。

                編譯$(PROJECT\jni\Application.mk來描寫敘述project情況,如模塊列表、CPU架構、是公布模式還是調試模式等。

                在$PROJECT\jni文件夾下調用$NDK\ndk-build來生成動態共享庫。假設NDK無法發現project路徑,那么通過NDK_PROJECT_PATH變量來指明project路徑就可以。

        (1)編譯腳本

                Android NDK的編譯很easy。其編譯腳本主要由ndk-build和ndk-gdb等構成,NDK_ROOT/build/core/init.mk和NDK_ROOT/build/core/main.mk也是比較重要的編譯腳本。NDK要求為GNU Make3.81及以上版本號。

                1)ndk-build

                        ndk-build是一個很重要的腳本,通常在設置APP_PROJECT_PATH后直接調用ndk-build就可以編譯project。如果要編譯的project為samples/hello-jni。編譯步驟例如以下:

                                #cd NDK_ROOT

                                #export APP_PROJECT_PATH=samples/hello-jni

                                #ndk-build

                        清除編譯出的中間文件的方法例如以下:

                                #ndk-build clean

                        強制編譯的方法例如以下:

                                #ndk-build -B V=1        //“B”表示強制又一次編譯,“V=1”表示顯示編譯命令

                        默認編譯出來的二進制代碼為Release版本號,假設希望調試代碼,則運行例如以下方法:

                                #ndk-build NDK_DEBUG=1        //NDK_DEBUG=1表示Debug版本號,NDK__DEBUG=0表示Release版本號

                        假設希望採用特定的Application.mk,則顯示聲明就可以。方法例如以下:

                                #ndk-build NDK_APP_APPLICATION_MK=<file>

                2)ndk-gdb

                        ndk-gdb調試和GDB調試一樣。均通過C/S模式進行調試。在Host端支持gdbclient,在target端放置gdbserver作為服務器端。

        (2)配置腳本

                NDK的配置腳本主要有Android.mk和Application.mk兩種。

                1)Android.mk

                        通過Android.mk,NDK能夠編譯靜態庫和動態庫等兩種輸出文件。可是僅僅有動態庫能夠被加入到應用中,靜態庫通經常使用於生成動態庫。NDK_ROOT/samples/hello-jni/jni下的Android.mk定義例如以下:

                                LOCAL_PATH:=$(call my-dir)        //設置LOCAL_PATH為當前路徑

                                include $(CLEAR-VARS)        //清除LOCAL_PATH外的全部本地變量

                                LOCAL_MODULE:=hello-jni        //定義模塊名

                                LOCAL_SRC_FILES:=hello-jni.c        //定義欲編譯的源碼

                                include $(BUILD_SHARED_LIBRARY)        //編譯我動態庫

                        當生成動態庫時。輸出文件為lib$(LOCAL_MODULE).so,當生成靜態庫時,輸出文件為lib$(LOCAL_MODULE).a。另外,LOCAL_MODULE名必須唯一,必須在編譯過程開始制定。通過LOCAL_MODULE_FILENAME能夠覆蓋LOCAL_MODULE。比如要覆蓋hello-jni為hello,方法例如以下:

                                LOCAL_MODULE:=hello-jni

                        注意:在LOCAL_MODULE_FILENAME中不應指定路徑和后綴名。Android NDK會自己主動處理LOCAL_MODULE_FILENAME:=libhello

                        (1)編譯配置

                                其實NDK還支持更復雜的配置。如設置編譯選項、載入共享庫、引入頭文件等。為了在編譯原生代碼時將全部錯誤視為警告。可運行例如以下方法:

                                         LOCAL_CFLAGS:= -Werror

                                通過LOCAL_LDLIBS能夠載入共享庫。為了設置日志輸出,須要載入liblog.so,方法例如以下:

                                         LOCAL_C_INCLUDES=<path>

                                         LOCAL_CFLAGS+=-I<path>

                        (2)處理器配置

                                眼下NDK支持的CPU架構包含armeabi、armeabi-v7a、x86等。

為了改啥多媒體和信號處理算法(如視頻編解碼、2D/3D圖形、游戲、音頻和語音處理、圖像處理、電話和語音合成等)的性能,通常希望打開NEON以支持ARM的高級單指令多數據流(SIMD。Single Instruction Multiple Data)引擎。NEON自ARMv6引入,打開NEON方法例如以下:

                                        LOCAL_ARM_NEON:=true        //要求設置TARGET_ARCH_ABI

                                在默認情況下。編譯出來的ARM架構的二進制文件時thumb模式的,其指令集是16位的,假設希望編譯出來的指令集是32位的,則能夠進行例如以下設置:

                                        LOCAL_ARM_MODE:=arm

                                另外通過在Application.mk中設置APP_OPTIM:=debug也能夠使編譯出來的指令為32位。這是由於在thumb模式下。調試器無法非常好地執行。

                                在輸出共享庫時,其路徑和CPU‘架構密切相關。結構為lib/<abi>/lib<name>.so。
                        (3)C++高級特性

                                假設希望應用C++異常,能夠進行例如以下設置:LOCAL_CPPFLAGS+=-fexceptions

                                假設希望應用RTTI,能夠進行例如以下設置:LOCAL_CPPFLAGS+=-frtti

                                NDK的部分特性。在Android.mk和Application.mk中均可設置

                2)Application.mk

                        Application.mk實際上是一個MakeFile文件,當中定義了欲編譯的模塊,主要變量包含APP_PROJECT、APP_BUILD_SCRICP、APP_MODULES、APP_OPTIM和APP_STL等。

                        (1)編譯配置

                                通過APP_PROJECT_PATH定義project的絕對路徑。

在默認情況下,Android NDK會到$(APP_PROJECT_PATH)\jni文件夾下尋找Android.mk文件並運行編譯,假設希望通過特定的Android.mk進行編譯,則需覆蓋APP_BUILD_SCRIPT。

                                APP_MODULES定義了由LOCAL_MODULE定義的模塊構成的模塊列表。

                                APP_OPTIM定義了編譯模式是release還是debug。

                        (2)處理器配置

                                在默認情況下,NDK支持的CPU架構為ARMv5TE,假設希望支持其它CPU結構。那么需顯式設置APP_ABI。比如,為了利用ARMv7的硬件浮點運算,應做例如以下配置:

                                        APP_ABI:=armeabi-v7a

                                armeabi-v7a在Android NDK R3時引入,其支持Thumb-2和VFPv兩種指令集擴展,假設希望同一時候支持ARMv5TE和ARMv7,可運行下面操作:

                                        APP_ABI:=armeabi armeabi-v7a

                        (3)C++高級特性

                                在默認情況下。盡管Android NDK僅包括了C++執行庫的一個最小子集(libstdc++.so)。可是開發人員能夠通過APP_STL來顯示載入STL實現。Android採用的STL實現為在SGI STL基礎上進行的一次封裝和針對各平台和編譯器優化后的STLport。方法例如以下:

                                        APP_STL:=stlport_static        //靜態的STLport庫

                                        APP_STL:=stlport_shared        //動態的STLport庫

                                        APP_STL:=system        //默認C++庫

                                載入STLport動態庫,通經常使用在project中有多個動態庫需用到STL特性的場景。這意味着在Java中,必須顯式載入STLport動態庫。

如果有libfoo.so和libbar.so都用到了STLport動態庫,事實上現例如以下:

                                        static{

                                                System.loadLibrary("stlport_shared");

                                                System.loadLibrary("foo");

                                                System.loadLibrary("bar");

                                        }

                                盡管Android NDK提供了STLport庫供載入,可是在少數場景中。依舊須要又一次編譯STLport庫。其方法例如以下:STLPORT_FORCE_REBUILD:=true

                                假設希望應用C++異常。能夠進行例如以下設置:APP_CPPFLAGS+=-fexceptions

                                假設希望應用RTTI,能夠進行例如以下設置:APP_CPPFLAGS+=-frtti

        (3)GDB調試

                為了進行原生代碼的調試,NDK提供了遠程GDB調試手段,其主要調試腳本為ndk-gdb。ndk-gdb僅在Foryo及以上版本號中收到支持。

                為了進行GDB調試,要求project處於debug模式下,這須要在AndroidManifest.xml文件里聲明android:debuggable屬性為true。同一時候在進行NDK編譯時生成的動態庫也是debug模式的。

                進行GDB調試的過程例如以下:

                        確保project處於debug模式。

                        通過ndk_build來編譯project,然后生成的APK安裝到模擬器中。

                        執行應用。

                        在應用的project路徑下執行ndk-gdb。

                ndk-gdb有非常多的選項。能夠支持執行應用中的特定Activity、指定ADB等。

        (4)NativeActivity實現

                在Android SDK中,提供了一個NativeActivity類。能夠與Android框架和原生代碼進行通信。實現NativeActivity有兩種方法,一是通過native_activity.h頭文件,二是通過android_native_glue.h頭文件。

從本質上講,NativeActivity的實現仍是基於JNI進行的。

                1)通過native_activity.h頭文件實現

                        在native_activity.h中定義創建NativeActivity(即ANativeActivity)所需的回調函數和數據結構,當中。回調函數會由應用程序的主線程進行處理,其側重Activity在生命周期的管理。當然和普通Activity一樣,假設設計欠妥,也會發生堵塞。這時會拋出ANR異常。

                        除了Activity的生命周期管理外。NativeActivity還支持輸入法的顯示和隱藏。還能夠通過AAssetManager支持斷言等。

                2)通過android_native_app_glue.h頭文件實現

                        Android通過android_native_app_glue.h提供了對NativeActivity的再封裝,還提供了構建NativeActivity所需的靜態助手函數,下圖是android_native_app_glue.h提供的NativeActivity封裝的結構。其主接入點為android_main()函數。

                               

                        ALooper用於Activity生命周期事件和輸入事件;輸入事件的隊列有AInputQueue維護;AConfiguration表示應用程序執行配置。ANativeWindow表示渲染所需的surface。至於通過android_native_app_glue.h頭文件實現NavtiveActivity的詳細過程。

4.應用程序編譯

        應用程序的編譯主要基於Android.mk文件進行。為了在編譯前清除遺留的中間文件,須要實現CleanSpec.mk。

        另外一個比較重要的配置文件為default.properties,其用於定義自己定義的配置。通常default.progerties由Android工具自己主動產生。不需開發人員干涉。當遺失default.properties文件時,工程會提示“Project has no default.properfies file! Edit the project properties to set one”,將無法編譯。Android的應用層代碼主要遵循APACHE2許可文件。

        (1)本地環境變量

                Android中存在大量的本地環境變量。

                        LOCAL_MODULE:表示本地模塊名,通經常使用於編譯源碼的模塊,在應用層開發中,多出如今JNI場景中。用於編譯動態庫、靜態庫。

                        LOCAL_PATH:表示本地路徑。通常在編譯模塊時表示當前編譯過程的根路徑,事實上現多為當前路徑,詳細應用例如以下:LOCAL_PATH:=$(call my-dir)

                        LOCAL_MODULE_TAGS:表示編譯的標簽,其可選值包含optional、eng、debug、tests、samples、user等。通常其值為optional。

                        LOCAL_MODULE_PATH:表示編譯輸出文件放置的位置。關於TARGET_OUT等的定義位於\build\core\文件夾下的ensetup.mk文件里。

                        LOCAL_MODULE_CALSS:表示模塊的類型,其可選值包含STATIC_LIBRARIES、EXECUTABLES、JAVA_LIBRARIES、ETC、SHARED_LIBRARIES等。

                        LOCAL_SRC_FILES:表示編譯的源文件。是主要的編譯變量。

                        LOCAL_SDK_VERSION:表示編譯的模塊的SDK版本號,默認值為current。假設希望編譯特定的SDK版本號的模塊。需顯式聲明SDK版本號。

                        LOCAL_PACKAGE_NAME:表示編譯的模塊名。在源碼下編譯應用時須要注意LOCAL_PACKAGE_NAME充當了編譯的開關的角色。

                        LOCAL_CERTIFICATE:表示採用的系統證書。可選值包含platform、shared、cts/tests/appsecurity-tests/certs/cts-testkey2、cts/tests/appsecurity-tests/certs/cts-testkey1、media等。當中platform表示系統證書。LOCAL_CERTIFICATE通常與AndroidManifest.xml中的android:shareduserId屬性同一時候使用。

                        LOCAL_SHARED_LIBRARIES:表示須要載入的動態庫,對於libstlport.so動態庫,載入方式為:LCOAL_SHARED_LIBRARIES:=libstlport

                        LOCAL_STATIC_LIBRARIES:表示須要載入的靜態庫,對於libc.a。其載入方式為:LOCAL_STATIC_LIBRARIES:=libc

                        LOCAL_PRELINK_MODULE:假設希望將模塊鏈接到系統中,則應設置LOCAL_PRELINK_MODULE為true。否則應設置LOCAL_PRELINK_MODULE為false。

                        CLEAR_VARS:有Android系統定義,用於清除除LOCAL_PATH外的全部本地變量。如LOCAL_SRC_FILES等。

        (2)Eclipse下編譯

                基於SDK在Eclipse下編譯應用程序,這是最主要的應用開發方法,相比基於源碼進行開發,這樣的開發調試起來更方便,也可充分利用IDE提供的自己主動完畢功能。可以有效地提高開發效率。

5.目標系統配置

        基於源碼開發,除了須要定義上層的目標環境配置外,針對不同的硬件,還需自己定義不同的目標板配置。目標板配置更側重底層的細節。

        (1)自己定義模擬器配置

                在商業開發中,依據產品的形態,自己定義模擬器是一個必須的過程,自己定義模擬器主要包含連個方面,自己定義皮膚和選項配置。

                對於QEMU和映像文件。在SDK環境下,假設創建AVD時選擇了Android 2.2平台,默認的QEMU為platforms/android-2.2/images/kernel-qemu,默認的映像文件為platforms\android-8\images\system.img;在源碼環境下。默認的QEMU為.\prebuilt\android0-arm\kernel\kernel-qemu。默認的映像文件為.\out\target\product\generic\system.img。

                須要注意的是隨着Android版本號的不斷升級,system.img已日趨龐大,在Foryo中個,system.img已經接近80MB,模擬器的啟動速度也越來越慢。

例如以下是加快模擬器啟動的方法:

                        使用RAID 0或RAMDISK等軟件來提高系統的I/O性能。

                        定期使用=wipe-data參數啟動emulator來重置模擬器數據。

                        假設基於SDK開發,那么不必每次都重新啟動模擬器。直接編譯應用,模擬器會自己主動更新應用的APK並啟動應用。

                        加大模擬器的config.ini文件里RAM的大小。

                1)自己定義皮膚

                         眼下Android已經提供多個分辨率下的皮膚,如HVGA、QVGA、WQVGA400、WQVGA432、WVGA800、WVGQ854等。

                         為了創建自己定義皮膚,首先須要搞清楚皮膚的構成。

通過觀察系統自帶的皮膚能夠知道。除了構建皮膚須要的圖片外。有兩個關鍵的配置文件,即hardware.ing和layout。當中hardware.int文件定義了模擬器相關的硬件信息。layout文件定義了模擬器相關的結構信息。

                         皮膚主要包含parts、layouts、keyboard、network等幾部分,當中還具體規定了背景圖和坐標,假設不須要某一部分,則直接從模擬器中去除就可以,對於橫豎屏處理,默認依據解析的第一中狀態進行載入,比如,當前產品為橫屏模式時。將portrait與landscape位置顛倒一下就可以。

在模擬器啟動后,通過“Ctrl+F11”快捷鍵就可以實現橫豎屏的切換。

                         假設高分辨率是HVGA,那么在所建project右擊。在彈出的快捷 菜單中運行Run as --- Run Confugurations 命令,可打開Android配置對話框選擇Target選項。在Additional Rmulator Command Line Options輸入框輸入參數 -skin HVGA-L也能夠實現豎屏啟動。

                         在自己定義皮膚時,依據目標設備的特征,能夠對系統皮膚進行相關的取舍和改動。

                         對於鍵盤,眼下Android默認支持qwerty、qwerty2兩種模式。並同意用戶自己定義鍵盤模式。假設希望自己定義鍵盤模式,則須要定義external/qemu/android/Charmap.c中的android_custom_charmap變量。

                         對於網絡速度,眼下Android支持full、GSM、HSCSD、GPRS、EDGE、UMTS、HSDPA等。

                         對於網路延遲,眼下Android提供了none、GPRS、EDGE、UMTS等。

                2)選項配置

                        模擬器的選項配置主要在模擬器皮膚的hardware.ini中定義,這是商業開發中重要一環,真實地模擬環境有助於降低開發中的隱患。

                        開發人員能夠依據自己的須要在創建AVD虛擬設備時定義選項配置,或者在hardware.ini中改動選項配置。


        (2)目標板配置

                目標板配置根詳細的硬件密切相關。通常芯片廠商會給出對應的參考實現,在generic目標環境中。目標板配置主要位於android\build\target\board\generic文件夾下。

                目標板配置可分為系統屬性、鍵盤布局、分區配置、板級配置等。

                1)系統屬性

                        在generic中。系統屬性主要分布在system.prop、device.mk等兩個文件里。system.prop中屬性的配置例如以下:

                                rild.libpath=/system/lib/libreference-ril.so

                                rild.libargs= -d /dev/ttyS0

                        device.mk中系統屬性的配置例如以下:

                                PRODUCT_PROPERTY_OVERRIDES :=ro.ril.hsxpa=1 ro.ril.gprsclass=10

                                PRODUCT_COPY_FILES:=development/data/etc/apns-conf.xml:system/etc/apns-conf.xml  development/data/etc/vold.conf:system/etc/vold.conf

                2)鍵盤布局

                        鍵盤的布局分布在目標環境的tuttle2.kcm、tuttle2.kl中。KCM文件表示按鍵字符集的映射。KL文件表示按鍵布局的映射。鍵盤文件的編譯工作由AndroidBoard.mk完畢。

                        KL文件里的內容是UTF-8類型的。格式為key SCANCODE KEYCODE[FLAGS...],當中SCANCODE表示按鍵掃描碼,KEYCODE表示鍵值,FLAGS可選項有SHIFT、ALT、ACPS、WAKE、WAKE_DROPPED等。

tuttle2.kl中的一個鍵值映射的示比例如以下:

                                key 158 BACK WAKE_DROPPED

                        為了節省空間。在編譯過程中會用工具makecharmap將KCM‘文件轉化為二進制文件*.bin。詳細的編譯情況能夠參考android/build/core/key_char_map.mk。

                3)分區配置

                        分區配置包含SD卡的掛載和設備內置的閃存分區配置。在generic中,沒有給出實際的閃存分區配置。

                        以下是vold.conf中關於SD卡的掛載配置

                                volume_sdcard{

                                       emu_media_path /devices/platform/goldfish_mmc.o/mmc_host/mmc0

                                       media_type mmc

                                       mount_point /sdcard

                                       ums_path   /device/platform/usb_mass_storage/lun0

                               }

                        在實際研發中。閃存分區配置多位於BoardConfigCommon.mk中。正常的分區的掛載則在init.rc中實現。

                4)板級配置

                        所謂板級配置主要指與設備相關的編譯期配置,其主要分布在BoardConfig.mk中。

         (3)目標環境配置

                Android默認支持的目標環境包含sim、full、generic等。另外,Android還給出了一些OEM廠商的目標環境演示樣例,如HTC的passion和Samsung的crespo。

                目標環境在源碼中的位置能夠分布在android\device、android\build\target等文件夾下,當中OEM’廠商的實現多位於android\device文件夾下。

                目標環境的制定是通過在build\core\文件夾下的envsetup.mk中聲明TARGET_PRODUCT來確定的。實際的目標環境配置會涉及非常多方面。OEM廠商能夠依據芯片廠商的參開設計進行調整。

6.文件系統配置

        在System\core\rootdir\文件夾下的Android.mk中會載入外部文件系統的掛載信息,相關信息定在vold.fstab文件里。

掛載文件系統的格式例如以下:

                dev_mount <label> <mount_point> <part> <sysfs_path1...>

        以下是vold.fstab的一個實現:

                dev_mount sdcard /mnt/sdcard 3 /devices/platform/s3c-sdhci.0/mmc_host/mmc0/mmc:0001/block/mmcblk0

7.編譯工具

        對於Android而言,包含Java、C/C++等源碼的編譯和幫組文檔的生成,整個編譯系統都是基於make進行的。

        (1)Java的編譯

                Java編譯涉及的工具比較簡單,編譯過程主要是基於Java進行的。在Froyo及曾經版本號中。採用的是Java5,在Gingerbread及以后的版本號中,採用的是Java 6,也能夠採用Open JDK 6。

                查看當前Java版本號的方法例如以下: #java -version

                在Linux中,假設同一時候安裝了Java 5和java 6,配置Java的工具update-java-alternatives。

                查看安裝Java的列表的方法例如以下: #update-java-alternatives -1

                設置當前Java的方法例如以下:#update-java-alternatives -s java-6-sun

        (2)C/C++的編譯

                為了使駐留的x86架構的操作系統上編譯的C/C++代碼的輸出文件可以在ARM架構的移動終端上執行,必須進行交叉編譯。通常編譯工具鏈由編譯器、連接器和解釋器構成,詳細到組件上,是由Binutils、GCC、Glibc和GDB構成的。在Android中。C庫採用的是Google優化自BSD的Bionic,而非標准的Glic。

                在Android中。原生代碼採用的交叉編譯工具鏈為arm-eabi-4.4.3,其可以將原生代碼編譯為ARM架構的二進制文件,其包含的主要工具有ar、as、c++、g++、ld、nm、objcopy、objdump、ranlib、strip等。

以下是交叉編譯工具鏈中主要工具的含義:

                       

                  在通過NDK編譯一些移植過來的代碼時須要注意,arm-eabi-4.4.3對代碼的要求更加嚴格,在低版本號編譯器下編譯通過的代碼在arm-eabi-4.4.3下並不一定能順利編譯通過。

                  當然在編譯模擬器部分的原生代碼時,並不須要進行交叉編譯,採用的編譯器為i686-unknown-linux-gnu-4.2.1。

                  在ARM架構下,編譯出的可運行文件的格式為ELF。假設希望構建自己的交叉編譯環境。則須要用到Crosstool工具。

                  假設希望檢測實現的原生代碼是否存在內存泄露,則會用到Valgrind工具。Valgrind是一個很強大的內存調試和代碼分析工具。

        (3)幫組文檔的生成

                  對於大型軟件,尤其是接口對外開放的軟件,幫組文檔尤為重要。所幸業界已經有專門的工具能夠幫組生成幫組文檔。當然在編寫幫組文檔時,須要依照相關的規定和要求進行。在Android中,採用的幫組文檔工具是javadoc。除了javadoc以外,眼下業界常常採用的幫組文檔工具還有Doxygen。

8.fastBoot模式

        fastboot即高速啟動。通經常使用於刷機、解鎖等操作中。一般特定的組合鍵來進入fastboot模式。因為fastboot跟硬件密切相關。故並不是全部的OEM廠商都遵循Android的規范。本節介紹的內容並不適合全部設備,僅供參考。

        在Froyo及以后版本號中,通過adb reboot bootloader能夠重新啟動物理Android設備。

        對於Nexus One、Nexus S、Nexus S 4G,在默認情況下設備時鎖住的,通過例如以下方法能夠解鎖:#fastboot oem unlock

        在Nexus S、Nexus S 4G中,通過fastboot還能夠鎖住設備,方法例如以下:#fastboot oem lock

        在升級系統后。可能會存在舊用戶數據和新系統不兼容的文件,解決方法例如以下: #fastboot erase cache   #fastboot erase userdata

        以下為升級系統的方法,這樣的方法導致引導分區、恢復分區、系統分區的重寫。在寫入過程完畢后重新啟動系統。 #fastboot flashall

        須要單獨編譯fastboot和adb時,運行的方法例如以下: #make fastboot adb

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM