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

 

posted @ 2017-05-16 21:24  jzdwajue  阅读(3777)  评论(0编辑  收藏  举报