标签:android系统编译
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
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:android系统编译
原文地址:http://blog.csdn.net/u011014707/article/details/46800893