Android的编译系统涉及面极广,包括编译工具、印像文件编译、SDK编译、NDK编译、目标系统配置等多个方面,虽然这些方面的内容烦琐而晦涩,可以参考的资料不多,但是系统设计尤其是系统架构人员必须熟悉它们。

1.源代码编译

        基于源代码的方式进行开发,通常会根据目标环境的不同,对系统配置进行调整,如采用不同的引导器、特定的驱动、不同的文件系统、特定的属性配置等,这就要求开发者必须熟练掌握源代码的编译方法和配置。

        (1)映像文件

                在编译完源代码后,需要将生成的文件等打包成相应的文件系统,然后烧写到移动终端。在Android中,默认的文件系统为YAFFS2,当然OEM厂商可以根据自己的需要,选择其他的文件系统。

                在源代码中,YAFFS2文件系统的实现位于external\yaffs2\yaffs2目录下,它针对大容量的NAND flash进行了优化,具有挂载时间短等优点。在当前的智能终端中,另一个比较常用的文件系统是UBIFS,它是由IBM和Nokia公司的工程师于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中的编译非常简单。除了基本的全系统编译外,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)源代码获取

                        获得源代码的方式非常简单,根据笔者的经验,建议创建两个文件夹,分别用于获取开发版本和最新版本。如果开发的目标环境是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的下载是基于单个工程进行的,在Android中,目前包含了170个左右的工程,其列表位于.repo\project.list中,处于开发阶段的工程和内核工程不会被下载。在下载完成前,源代码会被隐藏,直到下载完成后,开发者才能看到下载的源代码。

                        如果基于源代码的方式进行开发,内核的代码是必须的,通常可以从硬件厂商哪里获得最新的代码。当前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

                        对于单个工程,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来编写,原生代码通常以动态共享库的方式出现在Android工程中。通过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\jni下,并编写JNI接口代码。

                编写$(PROJECT\jni\Android.mk以描述编译过程,如需要编译的源文件、模块名等。在同一个Android.mk中可以定义多个模块。

                编译$(PROJECT\jni\Application.mk来描述工程情况,如模块列表、CPU架构、是发布模式还是调试模式等。

                在$PROJECT\jni目录下调用$NDK\ndk-build来生成动态共享库。如果NDK无法发现工程路径,那么通过NDK_PROJECT_PATH变量来指明工程路径即可。

        (1)编译脚本

                Android NDK的编译非常简单,其编译脚本主要由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即可编译工程。假设要编译的工程为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端支持gdb客户端,在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定义工程的绝对路径。在默认情况下,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动态库,通常用在工程中有多个动态库需用到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调试,要求工程处于debug模式下,这需要在AndroidManifest.xml文件中声明android:debuggable属性为true,同时在进行NDK编译时生成的动态库也是debug模式的。

                进行GDB调试的步骤如下:

                        确保工程处于debug模式。

                        通过ndk_build来编译工程,然后生成的APK安装到模拟器中。

                        运行应用。

                        在应用的工程路径下运行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,那么在所建工程右击,在弹出的快捷 菜单中执行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

 

Logo

开源、云原生的融合云平台

更多推荐