老罗的“Android应用程序资源的编译和打包过程分析”一文的一点总结

老罗这篇文章的原文地址:http://1025250620.iteye.com/blog/1974214


其中大部分内容并没有看明白。。。委屈,不过还是有一些东西需要总结一下。首先摘几段内容:


在前面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所示:

图1 Android应用程序资源的编译和打包过程

        这些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了。




Android资源打包工具的执行过程了,如图4所示:

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





 在Android资源中,有一种资源类型称为Public,它们一般是定义在res/values/public.xml文件中,形式如下所示:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <public type="string" name="string3" id="0x7f040001" />  
  4. </resources>  

        这个public.xml用来告诉Android资源打包工具aapt,将类型为string的资源string3的ID固定为0x7f040001。为 什么需要将某一个资源项的ID固定下来呢?一般来说,当我们将自己定义的资源导出来给第三方应用程序使用时,为了保证以后修改这些导出资源时,仍然保证第 三方应用程序的兼容性,就需要给那些导出资源一个固定的资源ID。

        每当Android资源打包工具aapt重新编译被修改过的资源时,都会重新给这些资源赋予ID,这就可能会造成同一个资源项在两次不同的编译中被赋予不 同的ID。这种情况就会给第三方应用程序程序带来麻烦,因为后者一般是假设一个ID对应的永远是同一个资源的。因此,当我们将自己定义的资源导出来给第三 方应用程序使用时,就需要通过public.xml文件将导出来的资源的ID固定下来。

        我们用一个例子来说public.xml文件的作用,考虑下面这个strings.xml文件:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="string1">String 1</string>  
  4.     <string name="string3">String 3</string>  
  5. </resources>  

        假设Android资源打包工具aapt为字符串资源项string1和string3分配到的资源ID如下所示:

  1. public final class R {  
  2.     // ...  
  3.     public static final class string {  
  4.         public static final int string1=0x7f040000;  
  5.         public static final int string3=0x7f040001;  
  6.     }  
  7. }  

        这时候第三方应用程序就会认为0x7f040001引用的永远是字符串“String 3”。

        假设将来的某一天,我们需要在strings.xml文件中增加一个新的字符串,如下所示:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="string1">String 1</string>  
  4.     <string name="string2">String 2</string>  
  5.     <string name="string3">String 3</string>  
  6. </resources>  

        如果没有上述的public.xml文件,那么Android资源打包工具aapt为字符串资源项string1、 string2和string3分配的资源ID就会如下所示:

  1. public final class R {  
  2.     // ...  
  3.     public static final class string {  
  4.         public static final int string1=0x7f040000;  
  5.         public static final int string2=0x7f040001;  
  6.         public static final int string3=0x7f040002// New ID! Was 0x7f040001  
  7.     }  
  8. }  

        这就完蛋了,这时候第三方应用程序通过0x7f040001引用到的字符串变成了“String 2”。

        如果我们使用上述的public.xml文件将字符串“String 3”固定为0x7f040001,那么Android资源打包工具aapt为字符串资源项string1、 string2和string3分配的资源ID就会如下所示:

  1. public final class R {  
  2.     // ...  
  3.     public static final class string {  
  4.         public static final int string1=0x7f040000;  
  5.         public static final int string2=0x7f040002;  
  6.         public static final int string3=0x7f040001// Resource ID from public.xml  
  7.     }  
  8. }  

        这样第三方应用程序通过0x7f040001引用到的字符串仍然是“String 3”。

        注意,我们在开发应用程序时,一般是不需要用到public.xml文件的,因为我们的资源基本上都是在内部使用的,不会导出来给第三方应用程序使用。只 在内部使用的资源,不管它的ID如何变化,我们都可以通过R.java文件定义的常量来正确地引用它们。只有系统定义的资源包才会使用到 public.xml文件,因为它定义的资源是需要提供给第三方应用程序使用的。



从图4中可以看出把所有资源文件都编译以后,最后几个步骤才是编译AndroidManifest.xml,之所以要在应用程序的所有资源项都编译完成之后,再编译应用程序的配置文件,是因为后者可能会引用到前者。

        应用程序配置文件AndroidManifest.xml的编译过程与其它的Xml资源文件的编译过程是一样的,可以参考前面的第七步。注意,应用程序配置文件AndroidManifest.xml编译完成之后,Android资源打包工具appt还会验证它的完整性和正确性,例如,验证AndroidManifest.xml的根节点mainfest必须定义有android:package属性。



最后是打包APK:

        所有资源文件都编译以及生成完成之后,就可以将它们打包到APK文件去了,包括:

        1. assets目录。

        2. res目录,但是不包括res/values目录, 这是因为res/values目录下的资源文件的内容经过编译之后,都直接写入到资源项索引表去了。

        3. 资源项索引文件resources.arsc。

        当然,除了这些资源文件外,应用程序的配置文件AndroidManifest.xml以及应用程序代码文件classes.dex,还有用来描述应用程 序的签名信息的文件,也会一并被打包到APK文件中去,这个APK文件可以直接拿到模拟器或者设备上去安装。

        至此,我们就分析完成Android应用程序资源的编译和打包过程了,其中最重要的是要掌握以下四个要点:

        1.  Xml资源文件从文本格式编译为二进制格式的过程。

        2. Xml资源文件的二进制格式。

        3. 资源项索引表resources.arsc的生成过程。

        4. 资源项索引表resources.arsc的二进制格式。


总结一下:

也就是说所有的res目录下所有的xml资源文件,以及AndroidManifest.xml,都会被编译到resources.arsc文件中,并且也会把它们的原文件(XML文件)再写入到APK中(除了res/values目录下的资源文件),而其它的所有资源文件也都会原封不动的打包到APK中。

这样的话如果用unzip直接解压APK文件的话,会有resources.arsc文件,还有其它所有资源文件的原文件,但是不会有res/values目录下的资源文件。

而如果用apktool反编译APK文件的话,所有的资源文件都会被还原,包括res/values目录下的资源文件,但不会有resources.arsc文件。


有点不太明白的是res/values/下的public.xml文件是什么时候生成的,按照上面说的它是对aapt打包工具有一定提示意义的,那表明它是在aapt对资源打包之前就有了,但是这时候应该还没有R.java,更不可能有public.xml吧?还有就是public.xml怎么用?


展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读