当前位置:文档之家› Android应用程序资源的编译和打包过程分析

Android应用程序资源的编译和打包过程分析

Android应用程序资源的编译和打包过程分析
Android应用程序资源的编译和打包过程分析

Android应用程序资源的编译和打包过程分析

作者:罗升阳

我们知道,在一个APK文件中,除了有代码文件之外,还有很多资源文件。这些资源文件是通过Android资源打包工具aapt(Android Asset Package Tool)打包到APK文件里面的。在打包之前,大部分文本格式的XML资源文件还会被编译成二进制格式的XML 资源文件。在本文中,我们就详细分析XML资源文件的编译和打包过程,为后面深入了解Android系统的资源管理框架打下坚实的基础。

在前面Android资源管理框架(Asset Manager)简要介绍和学习计划一文中提到,只有那些类型为res/animator、res/anim、res/color、res/drawable (非Bitmap文件,即非.png、.9.png、.jpg、.gif文件)、res/layout、res/menu、res/values和res/xml的资源文件均会从文本格式的XML文件编译成二进制格式的XML文件,如图1所示:

这些XML资源文件之所要从文本格式编译成二进制格式,是因为:

1. 二进制格式的XML文件占用空间更小。这是由于所有XML元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中去,并且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而可以减少文件的大小。

2. 二进制格式的XML文件解析速度更快。这是由于二进制格式的XML元素里面不再包含有字符串值,因此就避免了进行字符串解析,从而提高速度。

将XML资源文件从文本格式编译成二进制格式解决了空间占用以及解析效率的问题,但是对于Android资源管理框架来说,这只是完成了其中的一部分工作。Android资源管理框架的另外一个重要任务就是要根据资源ID来快速找到对应的资源。

在前面Android资源管理框架(Asset Manager)简要介绍和学习计划一文中提到,为了使得一个应用程序能够在运行时同时支持不同的大小和密度的屏幕,以及支持国际化,即支持不同的国家地区和语言,Android应用程序资源的组织方式有18个维度,每一个维度都代表一个配置信息,从而可以使得应用程序能够根据设备的当前配置信息来找到最匹配的资源来展现在UI上,从而提高用户体验。

由于Android应用程序资源的组织方式可以达到18个维度,因此就要求Android资源管理框架能够快速定位最匹配设备当前配置信息的资源来展现在UI上,否则的话,就会影响用户体验。为了支持Android资源管理框架快速定位最匹配资源,Android资源打包工具aapt在编译和打包资源的过程中,会执行以下两个额外的操作:

1. 赋予每一个非assets资源一个ID值,这些ID值以常量的形式定义在一个R.java 文件中。

2. 生成一个resources.arsc文件,用来描述那些具有ID值的资源的配置信息,它的内容就相当于是一个资源索引表。

有了资源ID以及资源索引表之后,Android资源管理框架就可以迅速将根据设备当

前配置信息来定位最匹配的资源了。接下来我们在分析Android应用程序资源的编译和打包过程中,就主要关注XML资源的编译过程、资源ID文件R.java的生成过程以及资源索引表文件resources.arsc的生成过程。

Android资源打包工具在编译应用程序资源之前,会创建一个资源表。这个资源表使用一个ResourceTable对象来描述,当应用程序资源编译完成之后,它就会包含所有资源

的信息。有了这个资源表之后, Android资源打包工具就可以根据它的内容来生成资源索引表文件resources.arsc了。

接下来,我们就通过ResourceTable类的实现来先大概了解资源表里面都有些什么东西,如图2所示:

图2 ResourceTable的实现

ResourceTable类用来总体描述一个资源表,它的重要成员变量的含义如下所示:--mAssetsPackage:表示当前正在编译的资源的包名称。

--mPackages:表示当前正在编译的资源包,每一个包都用一个Package对象来描述。例如,一般我们在编译应用程序资源时,都会引用系统预先编译好的资源包,这样当前正在编译的资源包除了目标应用程序资源包之外,就还有预先编译好的系统资源包。

--mOrderedPackages:和mPackages一样,也是表示当前正在编译的资源包,不过它们是以Package ID从小到大的顺序保存在一个Vector里面的,而mPackages是一个以Package Name为Key的DefaultKeyedVector。

--mAssets:表示当前编译的资源目录,它指向的是一个AaptAssets对象。

Package类用来描述一个包,这个包可以是一个被引用的包,即一个预先编译好的包,也可以是一个正在编译的包,它的重要成员变量的含义如下所示:

--mName:表示包的名称。

--mTypes:表示包含的资源的类型,每一个类型都用一个Type对象来描述。资源的类型就是指animimator、anim、color、drawable、layout、menu和values等。

--mOrderedTypes:和mTypes一样,也是表示包含的资源的类型,不过它们是Type ID从小到大的顺序保存在一个Vector里面的,而mTypes是一个以Type Name为Key的DefaultKeyedVector。

Type类用来描述一个资源类型,它的重要成员变量的含义如下所示:

--mName:表示资源类型名称。

--mConfigs:表示包含的资源配置项列表,每一个配置项列表都包含了一系列同名的资源,使用一个ConfigList来描述。例如,假设有main.xml和sub.xml两个layout类型的资源,那么main.xml和sub.xml都分别对应有一个ConfigList。

--mOrderedConfigs:和mConfigs一样,也是表示包含的资源配置项,不过它们是以Entry ID从小到大的顺序保存在一个Vector里面的,而mConfigs是以Entry Name

来Key的DefaultKeyedVector。

--mUniqueConfigs:表示包含的不同资源配置信息的个数。我们可以将mConfigs 和mOrderedConfigs看作是按照名称的不同来划分资源项,而将mUniqueConfigs看作是按照配置信息的不同来划分资源项。

ConfigList用来描述一个资源配置项列表,它的重要成员变量的含义如下所示:

--mName:表示资源项名称,也称为Entry Name。

--mEntries:表示包含的资源项,每一个资源项都用一个Entry对象来描述,并且以一个对应的ConfigDescription为Key保存在一个DefaultKeyedVector中。例如,假设有一个名称为icon.png的drawable资源,有三种不同的配置,分别是ldpi、mdpi和hdpi,那么以icon.png为名称的资源就对应有三个项。

Entry类用来描述一个资源项,它的重要成员变量的含义如下所示:

--mName:表示资源名称。

--mItem:表示资源数据,用一个Item对象来描述。

Item类用来描述一个资源项数据,它的重要成员变量的含义如下所示:

--value:表示资源项的原始值,它是一个字符串。

--parsedValue:表示资源项原始值经过解析后得到的结构化的资源值,使用一个Res_Value对象来描述。例如,一个整数类型的资源项的原始值为“12345”,经过解析后,就得到一个大小为12345的整数类型的资源项。

ConfigDescription类是从ResTable_config类继承下来的,用来描述一个资源配置信息。ResTable_config类的成员变量imsi、locale、screenType、input、screenSize、version 和screenConfig对应的实际上就是在前面Android资源管理框架(Asset Manager)简要介绍和学习计划一文提到的18个资源维度。

前面提到,当前正在编译的资源目录是使用一个AaptAssets对象来描述的,它的实现如图3所示:

图3 AaptAssets类的实现

AaptAssets类的重要成员变量的含义如下所示:

--mPackage:表示当前正在编译的资源的包名称。

--mRes:表示所包含的资源类型集,每一个资源类型都使用一个ResourceTypeSet 来描述,并且以Type Name为Key保存在一个KeyedVector中。

--mHaveIncludedAssets:表示是否有引用包。

--mIncludedAssets:指向的是一个AssetManager,用来解析引用包。引用包都是一些预编译好的资源包,它们需要通过AssetManager来解析。事实上,Android应用程序在运行的过程中,也是通过AssetManager来解析资源的。

--mOverlay:表示当前正在编译的资源的重叠包。重叠包是什么概念呢?假设我们正在编译的是Package-1,这时候我们可以设置另外一个Package-2,用来告诉aapt,如果Package-2定义有和Package-1一样的资源,那么就用定义在Package-2的资源来替换掉定义在Package-1的资源。通过这种Overlay机制,我们就可以对资源进行定制,而又不失一般性。

ResourceTypeSet类实际上描述的是一个类型为AaptGroup的KeyedVector,并且这个KeyedVector是以AaptGroup Name为Key的。AaptGroup类描述的是一组同名的资源,类似于前面所描述的ConfigList,它有一个重要的成员变量mFiles,里面保存的就是一系列同名的资源文件。每一个资源文件都是用一个AaptFile对象来描述的,并且以一个AaptGroupEntry为Key保存在一个DefaultKeyedVector中。

AaptFile类的重要成员变量的含义如下所示:

--mPath:表示资源文件路径。

--mGroupEntry:表示资源文件对应的配置信息,使用一个AaptGroupEntry对象来描述。

--mResourceType:表示资源类型名称。

--mData:表示资源文件编译后得到的二进制数据。

--mDataSize:表示资源文件编译后得到的二进制数据的大小。

AaptGroupEntry类的作用类似前面所描述的ResTable_config,它的成员变量mcc、mnc、locale、vendor、screenLayoutSize、screenLayoutLong、orientation、uiModeType、uiModeNight、density、tounscreen、keysHidden、keyboard、navHidden、navigation、screenSize和version对应的实际上就是在前面Android资源管理框架(Asset Manager)简要介绍和学习计划一文提到的18个资源维度。

了解了ResourceTable类和AaptAssets类的实现之后,我们就可以开始分析Android 资源打包工具的执行过程了,如图4所示:

图4 Android资源打包工具的执行过程

假设我们当前要编译的应用程序资源目录结构如下所示:

[plain]view plainc opy

1.project

2. --AndroidManifest.xml

3. --res

4. --drawable-ldpi

5. --icon.png

6. --drawable-mdpi

7. --icon.png

8. --drawable-hdpi

9. --icon.png

10. --layout

11. --main.xml

12. --sub.xml

13. --values

14. --strings.xml

接下来,我们就按照图4所示的步骤来分析上述应用程序资源的编译和打包过程。

一. 解析AndroidManifest.xml

解析AndroidManifest.xml是为了获得要编译资源的应用程序的包名称。我们知道,在AndroidManifest.xml文件中,manifest标签的package属性的值描述的就是应用程序的包名称。有了这个包名称之后,就可以创建资源表了,即创建一个ResourceTable对象。

二. 添加被引用资源包

Android系统定义了一套通用资源,这些资源可以被应用程序引用。例如,我们在XML布局文件中指定一个LinearLayout的android:orientation属性的值为“vertical”时,这个“vertical”实际上就是在系统资源包里面定义的一个值。

在Android源代码工程环境中,Android系统提供的资源经过编译后,就位于

out/target/common/obj/APPS/framework-res_intermediates/package-export.apk文件中,因此,在Android源代码工程环境中编译的应用程序资源,都会引用到这个

package-export.apk。

从上面的分析就可以看出,我们在编译一个Android应用程序的资源的时候,至少会涉及到两个包,其中一个被引用的系统资源包,另外一个就是当前正在编译的应用程序资源包。每一个包都可以定义自己的资源,同时它也可以引用其它包的资源。那么,一个包是

通过什么方式来引用其它包的资源的呢?这就是我们熟悉的资源ID了。资源ID是一个4

字节的无符号整数,其中,最高字节表示Package ID,次高字节表示Type ID,最低两字节表示Entry ID。

Package ID相当于是一个命名空间,限定资源的来源。Android系统当前定义了两个资源命令空间,其中一个系统资源命令空间,它的Package ID等于0x01,另外一个是应用程序资源命令空间,它的Package ID等于0x7f。所有位于[0x01, 0x7f]之间的Package ID都是合法的,而在这个范围之外的都是非法的Package ID。前面提到的系统资源包package-export.apk的Package ID就等于0x01,而我们在应用程序中定义的资源的Package ID的值都等于0x7f,这一点可以通过生成的R.java文件来验证。

Type ID是指资源的类型ID。资源的类型有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干种,每一种都会被赋予一个ID。

Entry ID是指每一个资源在其所属的资源类型中所出现的次序。注意,不同类型的资源的Entry ID有可能是相同的,但是由于它们的类型不同,我们仍然可以通过其资源ID来区别开来。

关于资源ID的更多描述,以及资源的引用关系,可以参考frameworks/base/libs/utils 目录下的README文件。

三. 收集资源文件

在编译应用程序资源之前,Android资源打包工具aapt会创建一个AaptAssets对象,用来收集当前需要编译的资源文件。这些需要编译的资源文件就保存在AaptAssets类的成员变量mRes中,如下所示:

[cpp]view plainc opy

1.class AaptAssets : public AaptDir

2.{

3. ......

4.

5.private:

6. ......

7.

8. KeyedVector >* mRes;

9.};

AaptAssets类定义在文件frameworks/base/tools/aapt/AaptAssets.h中。

AaptAssets类的成员变量mRes是一个类型为ResourceTypeSet的KeyedVector,这个KeyedVector的Key就是资源的类型名称。由此就可知,收集到资源文件是按照类型来保存的。例如,对于我们在这篇文章中要用到的例子,一共有三种类型的资源,分别是drawable、layout和values,于是,就对应有三个ResourceTypeSet。

从前面的图3可以看出,ResourceTypeSet类本身描述的也是一个KeyedVector,

不过它里面保存的是一系列有着相同文件名的AaptGroup。例如,对于我们在这篇文章中

要用到的例子:

1. 类型为drawable的ResourceTypeSet只有一个AaptGroup,它的名称为icon.png。这个AaptGroup包含了三个文件,分别是res/drawable-ldpi/icon.png、

res/drawable-mdpi/icon.png和res/drawable-hdpi/icon.png。每一个文件都用一个AaptFile 来描述,并且都对应有一个AaptGroupEntry。每一个AaptGroupEntry描述的都是不同的

资源配置信息,即它们所描述的屏幕密度分别是ldpi、mdpi和hdpi。

2. 类型为layout的ResourceTypeSet有两个AaptGroup,它们的名称分别为main.xml和sub.xml。这两个AaptGroup都是只包含了一个AaptFile,分别是

res/layout/main.xml和res/layout/sub.xml。这两个AaptFile同样是分别对应有一个AaptGroupEntry,不过这两个AaptGroupEntry描述的资源配置信息都是属于default的。

3. 类型为values的ResourceTypeSet只有一个AaptGroup,它的名称为strings.xml。这个AaptGroup只包含了一个AaptFile,即res/values/strings.xml。这个AaptFile也对应

有一个AaptGroupEntry,这个AaptGroupEntry描述的资源配置信息也是属于default的。

四. 将收集到的资源增加到资源表

前面收集到的资源只是保存在一个AaptAssets对象中,这一步需要将这些资源同时增加到一个资源表中去,即增加到前面所创建的一个ResourceTable对象中去,因为最后

我们需要根据这个ResourceTable来生成资源索引表,即生成resources.arsc文件。

注意,这一步收集到资源表的资源是不包括values类型的资源的。类型为values的资源比较特殊,它们要经过编译之后,才会添加到资源表中去。这个过程我们后面再描述。

从前面的图2可以看出,在ResourceTable类中,每一个资源都是分别用一个Entry 对象来描述的,这些Entry分别按照Pacakge、Type和ConfigList来分类保存。例如,对

于我们在这篇文章中要用到的例子,假设它的包名为“shy.luo.activity”,那么在ResourceTable类的成员变量mPackages和mOrderedPackages中,就会分别保存有一

个名称为“shy.luo.activity”的Package,如下所示:

[cpp]view plainc opy

1.class ResourceTable : public ResTable::Accessor

2.{

3. ......

4.

5.private:

6. ......

7.

8. DefaultKeyedVector > mPackages;

9. Vector > mOrderedPackages;

10.

11. ......

12.};

ResourceTable类定义在文件frameworks/base/tools/aapt/ResourceTable.h中。

在这个名称为“shy.luo.activity”的Package中,分别包含有drawable和layout两种类型的资源,每一种类型使用一个Type对象来描述,其中:

1. 类型为drawable的Type包含有一个ConfigList。这个ConfigList的名称为icon.png,包含有三个Entry,分别为res/drawable-ldip/icon.png、res/drawable-mdip/icon.png和

res/drawable-hdip/icon.png。每一个Entry都对应有一个ConfigDescription,用来描述不同的资源配置信息,即分别用来描述ldpi、mdpi和hdpi三种不同的屏幕密度。

2. 类型为layout的Type包含有两个ConfigList。这两个ConfigList的名称分别为main.xml和sub.xml。名称为main.xml的ConfigList包含有一个Entry,即

res/layout/main.xml。名称为sub.xml的ConfigList包含有一个Entry,即res/layout/sub/xml。

上述得到的五个Entry分别对应有五个Item,它们的对应关系以及内容如下图5所示:

图5 收集到的drawable和layout资源项列表

五. 编译values类资源

类型为values的资源描述的都是一些简单的值,如数组、颜色、尺寸、字符串和样式值等,这些资源是在编译的过程中进行收集的。接下来,我们就以字符串的编译过程来进行说明。

在这篇文章中要用到的例子中,包含有一个strings.xml的文件,它的内容如下所示:[html]view plainc opy

1.

2.

3.Activity

4.Sub Activity

5.Start sub-activity in process

6.Start sub-activity in new process

tring>

7.Finish activity

8.

这个文件经过编译之后,资源表就多了一个名称为string的Type,这个Type有五个ConfigList。这五个ConfigList的名称分别为“app_name”、“sub_activity”、

“start_in_process”、“start_in_new_process”和“finish”,每一个ConfigList又分别含有一个Entry。

上述得到的五个Entry分别对应有五个Item,它们的对应关系以及内容如图6所示:

图6 收集到的string资源项列表

六. 给Bag资源分配ID

类型为values的资源除了是string之外,还有其它很多类型的资源,其中有一些比较特殊,如bag、style、plurals和array类的资源。这些资源会给自己定义一些专用的值,

这些带有专用值的资源就统称为Bag资源。例如,Android系统提供的android:orientation 属性的取值范围为{“vertical”、“horizontal”},就相当于是定义了vertical和horizontal两个Bag。

在继续编译其它非values的资源之前,我们需要给之前收集到的Bag资源分配资源ID,因为它们可能会被其它非values类资源引用到。假设在res/values目录下,有一个attrs.xml文件,它的内容如下所示:

[html]view plainc opy

1.

2.

3.

4.

5.

6.

7.

这个文件定义了一个名称为“custom_orientation”的属性,它是一个枚举类型的属性,可以取值为“custom_vertical”或者“custom_horizontal”。Android资源打包工具aapt在编译这个文件的时候,就会生成以下三个Entry,如图7所示:

图7 收集到的Bag资源项列表

上述三个Entry均为Bag资源项,其中,custom_vertical(id类型资源)和custom_horizontal(id类型资源)是custom_orientation(attr类型资源)的两个bag,我们可以将custom_vertical和custom_horizontal看成是custom_orientation的两个元数据,用来描述custom_orientation的取值范围。实际上,custom_orientation还有一个内部元数据,用来描述它的类型。这个内部元数据也是通过一个bag来表示的,这个bag的名称和值分别为“^type”和TYPE_ENUM,用来表示它描述的是一个枚举类型的属性。注意,所有名称以“^”开头的bag都是表示一个内部元数据。

对于Bag资源来说,这一步需要给它们的元数据项分配资源ID,也就是给它们的bag 分配资源ID。例如,对于上述的custom_orientation来说,我们需要给它的^type、

custom_vertical和custom_horizontal分配资源ID,其中,^type分配到的是attr类型的资源ID,而custom_vertical和custom_horizontal分配到的是id类型的资源ID。

七. 编译Xml资源文件

前面的六步操作为编译Xml资源文件准备好了所有的素材,因此,现在就开始要编译Xml资源文件了。除了values类型的资源文件,其它所有的Xml资源文件都需要编译。这里我们只挑layout类型的资源文件来说明Xml资源文件的编译过程,也就是这篇文章中要用到的例子中的main.xml文件,它的内容如下所示:

[html]view plainc opy

1.

2.

3.android:orientation="vertical"

4.android:layout_width="fill_parent"

5.android:layout_height="fill_parent"

6.android:gravity="center">

7.

8.android:id="@+id/button_start_in_process"

9.android:layout_width="wrap_content"

10.android:layout_height="wrap_content"

11.android:gravity="center"

12.android:text="@string/start_in_process">

13.

14.

15.android:id="@+id/button_start_in_new_process"

16.android:layout_width="wrap_content"

17.android:layout_height="wrap_content"

18.android:gravity="center"

19.android:text="@string/start_in_new_process">

20.

21.

Xml资源文件main.xml的编译过程如图8所示:

图8 Xml资源文件的编译过程

1. 解析Xml文件

解析Xml文件是为了可以在内存中用一系列树形结构的XMLNode来表示它。XMLNode类的定义在文件frameworks/base/tools/aapt/XMLNode.h中,如下所示:

[cpp]view plainc opy

1.class XMLNode : public RefBase

2.{

3. ......

4.

5.private:

6. ......

7.

8. String16 mElementName;

9. Vector > mChildren;

10. Vector mAttributes;

11. ......

12. String16 mChars;

13. ......

14.};

每一个XMLNode都表示一个Xml元素,其中:

--mElementName,表示Xml元素标签。

--mChars,表示Xml元素的文本内容。

--mAttributes,表示Xml元素的属性列表。

--mChildren,表示Xml元素的子元素。

Xml文件解析完成之后,就可以得到一个用来描述根节点的XMLNode,接下来就可以通过这个根节点来完成其它的编译操作。

2. 赋予属性名称资源ID

这一步实际上就是给每一个Xml元素的属性名称都赋予资源ID。例如,对于main.xml 文件的根节点LinearLayout来说,就是要分别给它的属性名称“android:orientation”、“android:layout_width”、“android:layout_height”和“android:gravity”赋予一个资源ID。注意,上述这些属性都是在系统资源包里面定义的,因此,Android资源打包工具首先是要在系统资源包里面找到这些名称所对应的资源ID,然后才能赋给main.xml文件的根节点LinearLayout。

对于系统资源包来说,“android:orientation”、“android:layout_width”、“android:layout_height”和“android:gravity”等这些属性名称是它定义的一系列Bag资源,在它被编译的时候,就已经分配好资源ID了,就如上面的第六步操作所示。

每一个Xml文件都是从根节点开始给属性名称赋予资源ID,然后再给递归给每一个子节点的属性名称赋予资源ID,直到每一个节点的属性名称都获得了资源ID为止。

3. 解析属性值

上一步是对Xml元素的属性的名称进行解析,这一步是对Xml元素的属性的值进行解析。例如,对于对于main.xml文件的根节点LinearLayout来说,前面我们已经给它的属性android:orientation的名称赋予了一个资源ID,这里就要给它的值“vertical”进行解析。

前面提到,android:orientation是在系统资源包定义的一个Bag资源,这个Bag资源分配有资源ID,而且会指定有元数据,也就是它可以取哪些值。对于android:orientation 来说,它的合法取值就为“horizontal”或者“vertical”。在系统资源包中,“horizontal”或者“vertical”也同样是一个Bag资源,它们的值分别被定义为0和1。

Android资源打包工具是如何找到main.xml文件的根节点LinearLayout的属性android:orientation的字符串值“vertical”所对应的整数值1的呢?假设在上一步中,从系统

资源包找到“android:orientation”的资源ID为0x010100c4,那么Android资源打包工具就

会通过这个资源ID找到它的元数据,也就是两个名称分别为“horizontal”和“vertical”的bag,接着就根据字符串匹配到名称“vertical”的bag,最后就可以将这个bag的值1作为解析结果了。

注意,对于引用类型的属性值,要进行一些额外的处理。例如,对于main.xml文件

的第一个Button节点的android:id属性值“@+id/button_start_in_process”,其中,“@”表示后面描述的属性是引用类型的,“+”表示如果该引用不存在,那么就新建一个,“id”表示引用的资源类型是id,“button_start_in_process”表示引用的名称。实际上,在"id"前面,还可以指定一个包名,例如,将main.xml文件的第一个Button节点的android:id属性值指定为“@+[package:]id/button_start_in_process” 。如果没有指定包名的话,那么就会默认在当

前编译的包里面查找button_start_in_process这个引用。由于前面指有“+”符号,因此,如

果在指定的包里面找不到button_start_in_process这个引用的话,那么就会在该包里面创

建一个新的。无论button_start_in_process在指定的包里面原来就存在的,还是新建的,

最终Android资源打包工具都是将它的资源ID作为解析结果。

在我们这个情景中,在解析main.xml文件的两个Button节点的android:id属性值“@+id/button_start_in_process”和“@+id/button_start_in_new_process”时,当前正在编译

的资源包没有包含有相应的引用的,因此,Android资源打包工具就会在当前正在编译的资源包里面增加两个类型为id的Entry,如图9所示:

图9 增加两个类型为id的资源项

此外,对于main.xml文件的两个Button节点的android:text属性值

“@string/start_in_process”和“@string/start_in_new_process”,它们分别表示引用的是当前正在编译的资源包的名称分别为“start_in_process”和“start_in_new_process”的string资源。这两个string资源在前面的第五步操作中已经编译过了,因此,这里就可以直接获得它们的资源ID。

注意,一个资源项一旦创建之后,要获得它的资源ID是很容易的,因为它的Package ID、Type ID和Entry ID都是已知的。

4. 压平Xml文件

经过前面的三步操作之后,所需要的基本材料都已经准备好了,接下来就可以对Xml 文件的内容进行扁平化处理了,实际上就是将Xml文件从文本格式转换为二进制格式,这个过程如图10所示:

图10 压平Xml文件

将Xml文件从文本格式转换为二进制格式可以划分为六个步骤,接下来我们就详细分析每一个步骤。

Step 1. 收集有资源ID的属性的名称字符串

这一步除了收集那些具有资源ID的Xml元素属性的名称字符串之外,还会将对应的资源ID收集起来放在一个数组中。这里收集到的属性名称字符串保存在一个字符串资源池中,它们与收集到的资源ID数组是一一对应的。

对于main.xml文件来说,具有资源ID的Xml元素属性的名称字符串有“orientation”、“layout_width”、“layout_height”、“gravity”、“id”和“text”,假设它们对应的资源ID分别为0x010100c4、0x010100f4、0x010100f5、0x010100af、0x010100d0和0x0101014f,那么最终得到的字符串资源池的前6个位置和资源ID数组的对应关系如图11所示:

图11 属性名称字符串与属性资源ID的对应关系

Step 2. 收集其它字符串

这一步收集的是Xml文件中的其它所有字符串。由于在前面的Step 1中,那些具有资源ID的Xml元素属性的名称字符串已经被收集过了,因此,它们在一步中不会被重复收集。对于main.xml文件来说,这一步收集到的字符串如图12所示:

图12 其它字符串

其中,“android”是android命名空间前缀,

“https://www.doczj.com/doc/5a1378902.html,/apk/res/android”是android命名空间uri,“LinearLayout”是LinearLayout元素的标签,“Button”是Button元素的标签。

Step 3. 写入Xml文件头

最终编译出来的Xml二进制文件是一系列的chunk组成的,每一个chunk都有一个头部,用来描述chunk的元信息。同时,整个Xml二进制文件又可以看成一块总的chunk,它有一个类型为ResXMLTree_header的头部。

ResXMLTree_header定义在文件frameworks/base/include/utils/ResourceTypes.h 中,如下所示:

[cpp]view plainc opy

1./**

2. * Header that appears at the front of every data chunk in a resource.

3. */

4.struct ResChunk_header

5.{

6.// Type identifier for this chunk. The meaning of this value depends

7.// on the containing chunk.

8. uint16_t type;

9.

10.// Size of the chunk header (in bytes). Adding this value to

11.// the address of the chunk allows you to find its associated data

12.// (if any).

13. uint16_t headerSize;

14.

15.// Total size of this chunk (in bytes). This is the chunkSize plus

16.// the size of any data associated with the chunk. Adding this value

17.// to the chunk allows you to completely skip its contents (including

18.// any child chunks). If this value is the same as chunkSize, there is

19.// no data associated with the chunk.

20. uint32_t size;

21.};

22.

23./**

24. * XML tree header. This appears at the front of an XML tree,

25. * describing its content. It is followed by a flat array of

26. * ResXMLTree_node structures; the hierarchy of the XML document

27. * is described by the occurrance of RES_XML_START_ELEMENT_TYPE

28. * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array.

29. */

30.struct ResXMLTree_header

31.{

32.struct ResChunk_header header;

33.};

Ubuntu下Android源码修改、编译及运行、launcher定制

环境 Linux 版本:Ubuntu 11.04 (可由10.10的版本进行升级)64位系统 GCC版本:gcc version 4.5.2 Java版本:java version "1.6.0_26" 下载android源码前注意: 1、保证Ubuntu系统中的容量在80G左右,以保证足够的空间来存放android源码以及编译后的相关文件。 2、保证Ubuntu系统进行Internet访问。 联网方法:采用拨号进行连接。相关操作步骤如下所示: 1、虚拟机→设置→硬件→网络适配器→网络连接→桥接 2、启动Ubuntu系统,打开终端(在普通用户下),输入相关命令如下: $ pppoeconf //打开后输入上网账号跟密码,确认保存 $ sudo pon dsl-provider //上网连接命令 经过这两个步骤后就可以进行上网了。 Android源码编译所依赖的tools 01.$ sudo apt-get update 02.$ sudo apt-get -y install git-core 03.$ sudo apt-get -y install gnupg 04.$ sudo apt-get -y install sun-java6-jdk flex 05.$ sudo apt-get -y install bison 06.$ sudo apt-get -y install gperf 07.$ sudo apt-get -y install libsdl-dev 08.$ sudo apt-get -y install libesd0-dev 09.$ sudo apt-get -y install libwxgtk2.6-dev 10.$ sudo apt-get -y install build-essential 11.$ sudo apt-get -y install zip 12.$ sudo apt-get -y install curl 13.$ sudo apt-get -y install libncurses5-dev 14.$ sudo apt-get -y install zlib1g-dev 15.$ sudo apt-get -y install valgrind 注意:(如果是32bit的系统的话,则要更改几个Android.mk文件) 01./external/clearsilver/cgi/Android.mk 02./external/clearsilver/java-jni/Android.mk 03./external/clearsilver/util/Android.mk 04./external/clearsilver/cs/Android.mk 用gedit打开,修改m64为m32即可 另外 将build/core/main.mk中的ifneq (64,$(findstring 64,$(build_arch)))修改为: ifneq (i686,$(findstring i686,$(build_arch)))

Rockchip Android编译说明

v1.2.9 Android编译说明 1. 编译前预处理 (1) 1.1. 指定编译的产品名 (1) 1.2. 执行编译的预处理 (2) 1.3. 注意事项 (2) 2. 编译Android代码 (2) 3. 编译后处理 (3) 3.1. 生成FLASH烧写用的IMG (3) 3.2. 生成NFS用的文件目录 (3) 4. 在vendor下创建一个产品名 (4) 4.1. 新建产品目录 (4) 4.2. 功能裁剪 (4) 4.3. 注意事项 (5) 由于Android目录下可以包含多个产品的编译规则,所以编译源码要经过以下几个步骤:l编译前预处理(make prebuild) l编译Android代码(make) l编译后处理(make firmware) 1.编译前预处理 1.1. 指定编译的产品名 注意: 1.发布的Android SDK软件包已经做好以下配置,默认为sdkDemo产品,可以直接 跳到1.2的步骤。 2.如果要新建一个产品需要执行本小节1.1的操作。 3.新建产品请参见章节:在vendor下创建一个产品名 需要确认根目录下是否有buildspec.mk文件,如果有确认该文件内配置的产品名是否正确,例如sdkDemo的产品,则该文件内配置为如下值: 如果没有则拷($android)/build/buildspec.mk.default文件至根目录下,重复以上步骤。

1.2. 执行编译的预处理 在根目录下敲入make prebuild 该命令将执行一些产品相关功能的资源文件拷贝等操作。 1.3. 注意事项 l此部分步骤在只需要执行一次,后续编译不用再执行此命令。 l如何确认当前配置的产品名? 敲入make会立即弹出以下信息,确认下面的TARGET_PRODUCT字段值 l当前目录有多少的产品名? Android代码中,用户自定义扩展的产品名都存放在vendor目录下 2.编译Android代码 在根目录下敲入make

Android系统编译过程中常见问题汇总(2)

android源码编译常见问题 分类:android中级2013-03-09 16:20 397人阅读评论(2) 收藏举报编译过程: (在Ubuntu 11.04 64位机器上编译) 1. source build/envsetup.sh //初始化与环境envsetup.sh脚本 初始化完成,显示如下 including device/samsung/maguro/vendorsetup.sh including device/samsung/tuna/vendorsetup.sh including device/ti/panda/vendorsetup.sh including sdk/bash_completion/adb.bash 2. lunch full-eng //选择的目标 ============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=4.0.1 TARGET_PRODUCT=full TARGET_BUILD_VARIANT=eng TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm TARGET_ARCH_VARIANT=armv7-a HOST_ARCH=x86 HOST_OS=linux HOST_BUILD_TYPE=release BUILD_ID=ITL41D ============================================ //建立与一切使。GNU的make -JN参数可以并行处理任务,它是共同使用的任务数, //N的1倍和2倍之间是被用于建立计算机硬件线程数量。例如在E5520双机(2个CPU, //每个CPU 4核,每核心2线程),最快的构建与命令之间的J16和 -J32。 3. make -j4 编译完成

在Ubuntu上编译android

在Ubuntu7.10上编译android 1 概述 上个星期看到android开放源代码的消息,虽然觉得Google的这个动作会对业界产生很大影响,但没有时间仔细看,只浏览了Project layout。今天上午一个网友在mail中说已经编译、运行过android。我趁着中午休息时间,在一个Ubuntu7.10的虚拟机上编译、运行了一下。 我在这个虚拟机上编译过openmoko、poky。相对于这两个使用OpenEmbedded的平台,android的编译要简单一些,快一些。我在编译openmoko和poky时,将所有下载包都保存在一个目录中,将编译目录的sources子目录指向这个目录。这样做,一方面即使以后有些链接失效,也还可以编起来(其实编译时都不需要连接外网);另一方面在重新编译时,不用重新下载,可以加快编译速度。即使这样,完全编译一次poky也要一个晚上。编译android时,下载用了1个小时,编译也只需要1个小时。 2 编译中的两个小问题 2.1 python和JDK Google网站对编译过程的介绍还是很清晰的。不过在介绍其它软件包时,都给出了apt-get命令,却单独给出了python和JDK的链接地址,容易对读者产生误导,以为要手工安装这两个软件。其实这两个软件也应该通过apt-get安装。特别是python,如果从源代码安装,可能因为依赖其它软件,导致一些组件编译失败,影响android的编译。而且python 2.6的源代码包没有提供make uninstall,卸载也比较麻烦。其实我们只需要一个apt-get命令,就可以安装所有依赖的软件。 2.2 "fatal: git 1.5.4 or later required" 在Ubuntu7.10上执行repo init时,会出现"fatal: git 1.5.4 or later required"的错误提示。在Ubuntu8.04上编译没有这个问题。下面的流程里会介绍怎样解决这个问题。 3 在Ubuntu7.10上的编译流程

Android源码编译

Android源码编译 开发工具:Ubuntu10.04 在Ubuntu下使用root用户登录,注意root一开始是锁定的,使用如下命令解锁:sudopasswd root 按回车铵,提示输入当前密码,在提示输入二次密码。输入完成切换用户使用root用户登录。 Android源码结构 Makefile (全局的Makefile) bionic (Bionic含义为仿生,这里面是一些基础的库的源代码) bootloader(引导加载器) build (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具) dalvik (JAVA虚拟机) development(程序开发所需要的模板和工具) external (目标机器使用的一些库) frameworks (应用程序的框架层) hardware (与硬件相关的库) kernel (Linux2.6的源代码) packages (Android的各种应用程序) prebuil (Android在各种平台下编译的预置脚本) recovery (与目标的恢复功能相关) system (Android的底层的一些库) Out (编译好的文件) 编译Android源码必需的安装的软件包 g++库文件有可能Ubuntu自带,使用g++ -v命令查找是否安装,如果没有自带请手动安装,使用如下命令,sudo apt-get-install g++-4.4. 注意:g++分很多版本,如果使用了最新版本4.6将没办法编译,建议安装4.4版本 Gcc库文件有可能Ubuntu自带,使用gcc–v命令查找是否安装,如果没有自带使用如下命令手动安装,sudo apt-get-install gcc-4.4. 注意:gcc分很多版本,如果使用了最新版本4.6将没办法编译,建议安装4.4版本 不过还有一些应该是linux系统自带的,如果缺少就按照提示install一下sudo apt-get install build-essential sudo apt-get install make sudo apt-get install libc6-dev sudo apt-get install patch sudo apt-get install texinfo sudo apt-get install libncurses-dev

android4.1编译

Android 4.1.4 编译 1、下载源码: 我是在ics版本的基础上通过repo sync更新,发现有了an droid 4_1_r1的分支,等待更新完,repobranch切换分支,然后.Build/envsetup.she然后lunchi选择full-eng最后make,过一会报错。提示: build/core/product_config.mk:193:***_nic.PRODUCTS.[[build/target/product/v box_x 86.mk]]: "frameworks/native/build/phone-xhdpi-1024-dalvik-heap.mk" does not exist. Stop.** Don't have a product spec for: 'full' ** Do you have the right repo manifest? Google baidu bing 了大半天终于在an droid的bug单中找到了这条bug,解答是源码不全。愁了半天,repo sync都是最新的,愁了半天,最后再ics的repo 中重新下了个android 4.1.4的分支。 repoinit-uhttps: 4.1.1_r4 repo sync 下载3个小时,终于下载完成,本来下载源码需要很长时间5?10个小 时,由于再ics的基础上下载,只需要把patch下载就行了(repo会自动完成)。兴奋啊,赶紧. Build/envsetup.sh lunch 1 full-eng

/bin/bash: prebuilts/gcc/linux-x86/arm/arm-linux-androideabi- 4.6/bin/arm-linux- androideabi-gcc: 无法执行二进制文件 /bin/bash: prebuilts/gcc/linux-x86/arm/arm-linux-androideabi- 4.6/bin/arm-linux- androideabi-gcc: 无法执行二进制文件 /bin/bash: prebuilts/gcc/linux-x86/arm/arm-linux-androideabi- 4.6/bin/arm-linux- androideabi-gcc: 无法执行二进制文件 先没管它,继续机制行 make clean make -j8 过了几分钟提示错误:hostStaticLib: libhost(out/host/linux- x86/obj/STATIC_LIBRARIES/libhost_intermediates/libhost.a) ERROR: prebuilts/tools/gcc-sdk/../../gcc/linux-x86/host/x86_64-linux-glibc 2.7- 4.6/bin/x86_64-linux-ar only run on 64-bit linux make: ***[out/host/linux-x86/obj/STATIC_LIBRARIES/libhost_intermediates/libhost.a] 错误 1 host C: emulator-target-i386 <= external/qemu/cpus.c host C: emulator-target-i386 <= external/qemu/arch_init.c host C: emulator-target-i386 <= external/qemu/os-posix.c host C:

android源代码下载和编译教程

Android源代码由两部分组成,其一是Android系统应用部分的代码,其次是Android 所使用的内核代码,如果针对Google Nexus S来开发ROM,就需要下载三星的内核代码Android 系统应用代码: https://https://www.doczj.com/doc/5a1378902.html,/platform/manifest Android内核代码: https://https://www.doczj.com/doc/5a1378902.html,/kernel/samsung.git 准备阶段:设置Linux编译环境(本人是Ubuntu 11.10 ,32位系统),一般你需要以下配置: Python 2.5 -- 2.7. GNU Make 3.81 -- 3.82,, JDK 6 (for Gingerbread or newer); JDK 5( for Froyo or older). Git 1.7 or newer. 1、安装一些必备的包。 Git是Linux Torvalds为了帮助管理Linux内核而开发的一个开放源码的分布式版本控制系统软件,它不同于SVN,CVS这样集中式的版本控制系统。在集中式版本控制系统中只有一个仓库(repository),许多个工作目录,而git这样的分布式控制系统中,每一个工作目录都包含一个完整仓库,它们支持离线工作,本地提交可以稍后提交到服务器上。分布式系统理论上也比集中式的单服务器系统更健壮,单服务器系统一旦服务器出现问题整个系统就不能运行了,分布式系统通常不会因为一两个节点而收到影响? 因为Android是由kernel,Dalvik,Bionic,prebuild,build等多个Git项目组成, 所以Android项目编写了一个名为repo的python的脚本来统一管理这些项目的仓库,使得Git的使用更加简单。 $ sudo apt-get install git-core gnupg flex bison gperf build-essential \ zip curl zlib1g-dev libc6-dev lib32ncurses5-dev ia32-libs \ x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev \ libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown \ libxml2-utils xsltproc $ sudo apt-get install libx11-dev:i386 2、创建源代码目录 首先确保在当前用户的主目录下创建一个/bin目录(如果没有的话),然后把它加到PATH 环境变量中: $ mkdir ~/bin $ PATH=~/bin:$PATH 或者$export PATH=$PATH:~/bin 3、用curl下载repo脚本,并保存在~/bin/repo文件中: $ curl https://https://www.doczj.com/doc/5a1378902.html,/dl/googlesource/git-repo/repo > ~/bin/repo 为repo加上执行权限 $ chmod a+x ~/bin/repo 4、初始化版本库 首先建立一个目录,用来存放下载下来的东西 $mkdir MyAndroid $cd MyAndroid 以后,执行如下命令 $ repo init -u https://https://www.doczj.com/doc/5a1378902.html,/platform/manifest -b android-2.3.7_r1 我这里下载的是android2.3.7版本的,所用的参数是android-2.3.7_r1。由于最新的是android-4.0.3_r1,所以使用上面即可下载2.3.7的源代码 注意:如果要是把所有的版本都下载下来,那时间是需要很久很久的,需要耐心等待,但是如果仅仅是下载某一个版本的话,还是很快就可以了。 最后,如果看到repo initialized in /MyAnroid的提示,说明本地的版本库已经初始化成功了(期间需要你填写姓名和Gmail地址)。

Ubuntu下编译Android源码全过程

Ubuntu下编译Android源码全过程(转) 源码, 编译, Ubuntu, Android 一、获取Android源代码 Git是LinuxTorvalds(Linux之父)为了帮助管理Linux内核开发而开发的一个开放源码的分布式版本控制软件,它不同于Subversion、CVS这样的集中式版本控制系统。在集中式版本控制系统中只有一个仓库(Repository),许多个工作目录(WorkingCopy),而在Git这样的分布式版本控制系统中(其他主要的分布式版本控制系统还有BitKeeper、Mercurial、GNUArch、Bazaar、Darcs、SVK、Monotone等),每一个工作目录都包含一个完整仓库,它们支持离线工作,本地提交可以稍后提交到服务器上。 因为Android是由Kernel、Dalvik、Bionic、Prebuilt、build等多个项目组成,如果我们分别使用Git来逐个获取显得很麻烦,所以Android项目编写了一个名为Repo的Python 的脚本来统一管理这些项目的仓库,使得项目的获取更加简单。 在Ubuntu 8.04上安装Git只需要设定正确的更新源,然后使用apt-get就可以了,apt-get 是一条Linux命令,主要用于自动从互联网的软件仓库中搜索、安装、升级、卸载软件或 操作系统。 apt-get命令一般需要root权限执行,所以一般跟着sudo命令。 sudo apt-get install git-core curl 这条命令会从互联网的软件仓库中安装git-core和curl。 其中curl是一个利用URL语法在命令行方式下工作的文件传输工具,它支持很多协议,包括FTP、FTPS、HTTP、HTTPS、TELENT等,我们需要安装它从网络上获取Repo脚本文件。 curl https://www.doczj.com/doc/5a1378902.html,/repo >~/bin/repo 这句命令会下载repo脚本文件到当前主目录的/bin目录下,并保存在文件repo中。 最后我们需要给repo文件可执行权限

Android ninja 编译启动过程分析

Android ninja编译启动过程分析 ---make是如何转换到到ninja编译的 1.首先你的得对make的工作机制有个大概的了解: 运行的命令在要编译的目录下运行make,或者make target_name a.分析处理保存阶段(没有实际编译动作):它首先对当前目录下的Makefile文件的做一次扫描,语法分析,还有处理,主要是变量的保存,目标依赖列表生成,目标下的action列表的生成,然后记住 b.然后按记住的目标执行action列表动作(有实际编译动作). 编译启动的入口方式还是运行make: 2开始make-jxxx方式进入.....(xxx是本机cpu的数量) make开始做进行第一次扫描.... 目前USE_NINJA还是没有定义,估计以后很久很久才能启用的了! BUILDING_WITH_NINJA开始也是没定义的 看make扫描入口文件: Makefile: include build/core/main.mk 在build/core/main.mk: 在ninia之前都有include help.mk和config.mk 97include$(BUILD_SYSTEM)/help.mk 98 99#Set up various standard variables based on configuration 100#and host information. 101include$(BUILD_SYSTEM)/config.mk 说明make help//显示make帮助make config//当前显示配置 103relaunch_with_ninja:= 104ifneq($(USE_NINJA),false) 105ifndef BUILDING_WITH_NINJA<==第二次扫描不会到这里了 106relaunch_with_ninja:=true 107endif 108endif 116ifeq($(relaunch_with_ninja),true)<===第一次扫描入这里了 117#Mark this is a ninja build. 118$(shell mkdir-p$(OUT_DIR)&&touch$(OUT_DIR)/ninja_build) 119include build/core/ninja.mk//---进入ninja.mk 第一次扫描到此为止就结束掉了,因为在当前ifeq else endif后面没有代码了 120else#///!relaunch_with_ninja<===第二次扫描入这里了

android自动编译环境搭建

Win8 配置Android开发环境及自动编译环境 一、安装JDK 1,下载JDK:https://www.doczj.com/doc/5a1378902.html,/technetwork/java/javase/downloads/index.html 点击,在新页面选择 windows x64(如下)进入下载。 2,下载后安装。

3,Windows 下安装完毕后需要设置三个环境变量 JAVA_HOME 指明JDK安装路径,就是刚才安装时的路径C:\Program Files\Java\jdk1.7.0,此路径下包括lib,bin,jre等文件夹(此变量最好设置,因为以后运行tomcat,eclipse,ant 等都需要使用 此变量) PATH Path使得系统可以在任何路径下识别java命令,设为: %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin CLASSPATH CLASSPATH为java加载类(class or lib)路径,只有类在classpath中,java命令才能识别,设为: .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar (要加.表示当前路径) %JAVA_HOME%就是引用前面指定的JAVA_HOME。 上述环境变量设置好了后,可以运行cmd,在命令窗口里面运行下面命令,可以看到类似下面的信息: 当然也可以在命令行下面使用java -fullversion 可以对当前版本的详细信息。这时候的返回信息类似如下: java full version "1.7.0-b147" 之后我们就可以写一个超简单的java程序: HelloWorld.java 文件内容如下: public class HelloWorld { public static void main(String args[]) { System.out.println("HelloWorld!"); } } 依次:javac HelloWorld.java java HelloWorld

Android编译过程详解

本文使用Android版本为2.1,采用开发板为华清远见研发的FS_S5PC100 A8开发板。+--------------------------------------------------------------------------------------------------------------------+ 1. source build/envsetup.sh 这个命令是用来将envsetup.sh里的所有用到的命令加载到环境变量里去,我们来分析下它。envsetup.sh里的主要命令如下: ?

根据上面的内容,可以推测出,如果要想定义自己的产品编译项,简单的办法是直接在envset up.sh最后,添加上add_lunch_combo myProduct-eng,当然这么做,不太符合上面代码最后的本意,我们还是老实的在vendor目录下创建自己公司名字,然后在公司目录下创建一个新的vendorsetup.sh,在里面添加上自己的产品编译项 ? 这样,当我们在执行source build/envsetup.sh命令的时候,可以在shell上看到下面的信息: ?

2. 按照android官网的步骤,开始执行lunch full-eng 当然如果你按上述命令执行,它编译的还是通用的eng版本系统,不是我们个性系统,我们可以执行lunch命令,它会打印出一个选择菜单,列出可用的编译选项 如果你按照第一步中添加了vendorsetup.sh那么,你的选项中会出现: ? 其中第3项是我们自己添加的编译项。 lunch命令是envsetup.sh里定义的一个命令,用来让用户选择编译项,来定义Product和编译过程中用到的全局变量。 我们一直没有说明前面的fs100-eng是什么意思,现在来说明下,fs100是我定义的产品的名字,eng是产品的编译类型,除了eng外,还有user, userdebug,分别表示: eng: 工程机, user:最终用户机 userdebug:调试测试机

Android应用程序资源的编译和打包过程分析

Android应用程序资源的编译和打包过程分析 作者:罗升阳 我们知道,在一个APK文件中,除了有代码文件之外,还有很多资源文件。这些资源文件是通过Android资源打包工具aapt(Android Asset Package Tool)打包到APK文件里面的。在打包之前,大部分文本格式的XML资源文件还会被编译成二进制格式的XML 资源文件。在本文中,我们就详细分析XML资源文件的编译和打包过程,为后面深入了解Android系统的资源管理框架打下坚实的基础。 在前面Android资源管理框架(Asset Manager)简要介绍和学习计划一文中提到,只有那些类型为res/animator、res/anim、res/color、res/drawable (非Bitmap文件,即非.png、.9.png、.jpg、.gif文件)、res/layout、res/menu、res/values和res/xml的资源文件均会从文本格式的XML文件编译成二进制格式的XML文件,如图1所示: 这些XML资源文件之所要从文本格式编译成二进制格式,是因为: 1. 二进制格式的XML文件占用空间更小。这是由于所有XML元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中去,并且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而可以减少文件的大小。 2. 二进制格式的XML文件解析速度更快。这是由于二进制格式的XML元素里面不再包含有字符串值,因此就避免了进行字符串解析,从而提高速度。

将XML资源文件从文本格式编译成二进制格式解决了空间占用以及解析效率的问题,但是对于Android资源管理框架来说,这只是完成了其中的一部分工作。Android资源管理框架的另外一个重要任务就是要根据资源ID来快速找到对应的资源。 在前面Android资源管理框架(Asset Manager)简要介绍和学习计划一文中提到,为了使得一个应用程序能够在运行时同时支持不同的大小和密度的屏幕,以及支持国际化,即支持不同的国家地区和语言,Android应用程序资源的组织方式有18个维度,每一个维度都代表一个配置信息,从而可以使得应用程序能够根据设备的当前配置信息来找到最匹配的资源来展现在UI上,从而提高用户体验。 由于Android应用程序资源的组织方式可以达到18个维度,因此就要求Android资源管理框架能够快速定位最匹配设备当前配置信息的资源来展现在UI上,否则的话,就会影响用户体验。为了支持Android资源管理框架快速定位最匹配资源,Android资源打包工具aapt在编译和打包资源的过程中,会执行以下两个额外的操作: 1. 赋予每一个非assets资源一个ID值,这些ID值以常量的形式定义在一个R.java 文件中。 2. 生成一个resources.arsc文件,用来描述那些具有ID值的资源的配置信息,它的内容就相当于是一个资源索引表。 有了资源ID以及资源索引表之后,Android资源管理框架就可以迅速将根据设备当 前配置信息来定位最匹配的资源了。接下来我们在分析Android应用程序资源的编译和打包过程中,就主要关注XML资源的编译过程、资源ID文件R.java的生成过程以及资源索引表文件resources.arsc的生成过程。 Android资源打包工具在编译应用程序资源之前,会创建一个资源表。这个资源表使用一个ResourceTable对象来描述,当应用程序资源编译完成之后,它就会包含所有资源 的信息。有了这个资源表之后, Android资源打包工具就可以根据它的内容来生成资源索引表文件resources.arsc了。 接下来,我们就通过ResourceTable类的实现来先大概了解资源表里面都有些什么东西,如图2所示:

利用mm命令编译Android模块

在Android开发过程中,当我们只需要编译某个修改过的模块时,使用mm命令可以使你不用编译整个Android项目,而只需单独编译该模块即可,这样速度是不是很快呢?具体的步骤如下: 1. 在Android源代码目录下: [c-sharp]view plaincopy 1cd mydroid 2source build/envsetup.sh 也可以将source ~/mydroid/build/envsetup.sh放到~/.bashrc中,这样每次打开shell时就会自动运行该脚本。 2. 在shell中使用help命令查看可以运行的命令 [c-sharp]view plaincopy 3# help 4Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment: 5- croot: Changes directory to the top of the tree. 6- m: Makes from the top of the tree. 7- mm: Builds all of the modules in the current directory. 8- mmm: Builds all of the modules in the supplied directories. 9- cgrep: Greps on all local C/C++ files. 10- jgrep: Greps on all local Java files. 11- resgrep: Greps on all local res/*.xml files. 12- godir: Go to the directory containing a file. 13 14Look at the source to view more functions. The complete list is: 15add_lunch_combo cgrep check_product check_variant choosecombo chooseproduct choosetype choosevariant cproj croot findmakefile gdbclient get_abs_build_var getbugreports get_build_var getprebuilt gettop godir help isviewserverstarted jgrep

Android的编译系统

设为主页加入收藏帮助 | 留言交流 转藏到我的图书馆推荐给朋友举报

build/core/envsetup.mk generate dir config and so on build/target/product product config build/target/board board config build/core/combo build flags config 这里解释下这里的board和product。borad主要是设计到硬件芯片的配置,比如是否提供硬件的某些功能,比如说GPU等等,或者芯片支持浮点运算等等。product是指针对当前的芯片配置定义你将要生产产品的个性配置,主要是指APK方面的配置,哪些APK会包含在哪个product中,哪些APK在当前product中是不提供的。 config.mk是一个总括性的东西,它里面定义了各种module编译所需要使用的HOST工具以及如何来编译各种模块,比如说BUILT_PREBUILT就定义了如何来编译预编译模块。envsetup.mk主要会读取由envsetup.sh写入环境变量中的一些变量来配置编译过程中的输出目录,combo里面主要定义了各种Host和Target结合的编译器和编译选项。 配置部分主要完成以下几个工作: a) 基于Android 产品的配置(product config):选择构建安装的运行程序(user package) b) 设置 target 等相关变量TARGET_ARCH, TARGET_OS,TARGET_BUILD_TYPE, TARGET_PREBUILT_TAG c) 根据编译环境设置 host等相关变量HOST_OS, HOST_ARCH,HOST_BUILD_TYPE, HOST_PREBUILT_TAG d) 编译 target上运行程序所需的工具链及编译参数设置,如linux-arm-cc,cflag,include目录等。 e) 编译 host上运行程序所需的工具链及编译参数设置。 下图简要介绍了Android build system的配置部分的主要构成及相互关系。 流程-... 男性必吃的12种食 物!... 美食 哲理小品:最美妙 的人生 摄影基础技法

Ubuntu下Android源码修改、编译及运行、launcher定制

环境 Linux版本: Ubuntu 11.04(可由 10.10的版本进行升级)64位系统 GCC版本: gcc version 4.5.2 Java版本: java version " 1.6.0_26" 下载android源码前注意: 1、保证Ubuntu系统中的容量在80G左右,以保证足够的空间来存放android源码以及编译后的相关文件。 联网方法: 采用拨号进行连接。相关操作步骤如下所示: 1、虚拟机设置硬件网络适配器网络连接桥接 2、启动Ubuntu系统,打开终端(在普通用户下),输入相关命令如下: $ pppoeconf//打开后输入上网账号跟密码,确认保存 $ sudo pon dsl-provider//上网连接命令

经过这两个步骤后就可以进行上网了。 Android源码编译所依赖的tools 01.$ sudo apt-get update 02.$ sudo apt-get -y install git-core 03.$ sudo apt-get -y install gnupg 04.$ sudo apt-get -y install sun-java6-jdk flex 05.$ sudo apt-get -y install bison 06.$ sudo apt-get -y install gperf 07.$ sudo apt-get -y install libsdl-dev 08.$ sudo apt-get -y install libesd0-dev 09.$ sudo apt-get -y install libwxgtk 2.6-dev 10.$ sudo apt-get -y install build-essential 11.$ sudo apt-get -y install zip 12.$ sudo apt-get -y install curl 13.$ sudo apt-get -y install libncurses5-dev 14.$ sudo apt-get -y install zlib1g-dev 15.$ sudo apt-get -y install valgrind 注意: (如果是32bit的系统的话,则要更改几个Android.mk文件)01./external/clearsilver/cgi/Android.mk

Android 源码编译 流程

Android 源码编译调试流程 by mengke 1 编译流程 sudo apt-get install build-essential sudo apt-get install make sudo apt-get install gcc sudo apt-get install g++ sudo apt-get install libc6-dev sudo apt-get install patch sudo apt-get install texinfo sudo apt-get install libncurses-dev sudo apt-get install git-core gnupg sudo apt-get install flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl sudo apt-get install ncurses-dev sudo apt-get install zlib1g-dev sudo apt-get install valgrind sudo apt-get install python2.5 安装java环境 sudo apt-get install sun-java6-jre sun-java6-plugin sun-java6-fonts sun-java6-jdk sudo apt-get install sun-java5-jdk (ubuntu 910 估计会有一些问题) (注:官方文档说如果用sun-java6-jdk可出问题,得要用sun-java5-jdk。经测试发现,如果仅仅make(make不包括make sdk),用sun-java6-jdk是没有问题的。而make sdk,就会有问题,严格来说是在make doc出问题,它需要的javadoc版本为1.5。 因此,我们安装完sun-java6-jdk后最好再安装sun-java5-jdk,或者只安装sun-java5-jdk。这里sun-java6-jdk和sun-java5-jdk都安装,并只修改javadoc.1.gz和javadoc。因为只有这两个是make sdk用到的。这样的话,除了javadoc工具是用1.5版本,其它均用1.6版本: sudo apt-get install sun-java5-jdk) cd /etc/alternatives sudo rm javadoc.1.gz sudo ln -s /usr/lib/jvm/java-1.5.0-sun/man/man1/javadoc.1.gz javadoc.1.gz sudo rm javadoc sudo ln -s /usr/lib/jvm/java-1.5.0-sun/bin/javadoc javadoc 假设源代码的目录为mydroid root@mk-desktop:~/mydroid# ls Makefile build development frameworks out sdk bionic cts device hardware packages system bootable dalvik external ndk prebuilt vendor

相关主题
文本预览
相关文档 最新文档