Android资源管理框架-------之总述(一)

        我们知道对于一个APK而言,主要就两部分:DEX字节码和资源。其中,为了让开发者在不同的环境下更加方便地管理和使用资源,Android专门提供了一套资源管理的框架。关于Android的资源,我们在Android资源管理概述中有过简单描述,网上也有大量介绍,我们就不再赘述。 本篇,我们先介绍一下这个框架(因其主要接口封装在AssetManager这个类中,我们就叫这个框架AssetManager了,后面不再说明)的基本概念和常识。

功能

        Android资源管理框架(AssetManager)主要负责资源的加载、组织、管理等,并对上层提供这些功能相关的接口。其中资源的加载包括对APK包中资源相关的文件的读取、解析,比如resources.arsc文件的加载、Asset资源的打开等等;资源的组织,主要是指一些资源相关的信息被加载到内存中后,这些信息是如何存放的,放在哪些数据结构中;资源的管理则包括我们对于已经加载的这些资源信息的获取,资源包的添加等等。

核心文件

        Android资源管理框架(AssetManager)涉及到的核心文件包括:

  • frameworks/base/core/java/android/content/res/Resources.java这个是我们经常使用的Android资源管理的框架的接口类,它封装了Android资源管理的大部分功能。比如资源相关信息的获取、资源文件的打开、具体资源的创建、Theme相关的接口等等。
  • frameworks/base/core/java/android/content/res/AssetManager.java AssetManager.java是比Resources.java更低级的资源管理相关的接口。Resources.java中的很多方法就是对AssetManager.java的封装。
  • frameworks/base/core/jni/android_util_AssetManager.cpp及其头文件 AssetManager.java中的绝大多数逻辑是在native层实现的,所以就有了这个JNI层。
  • frameworks/base/libs/androidfw/AssetManager.cpp及其头文件 AssetManager.cppAssetManager.java在native层的具体实现,同时会持有AssetManager.cpp中AssetManager对象的地址。
  • frameworks/base/libs/androidfw/ResourceTypes.cpp及其头文件 ResourceTypes.cpp里包括了与resources.arsc和内存中资源相关信息的大量数据结构,同时大部分资源管理相关的逻辑也在这里实现,可以说是Android资源管理模块中最重要的文件。
  • frameworks/base/cmds/idmap目录下的文件 idmap的概念和功能我们在Android资源管理中的Runtime Resources Overlay-------之概述(一)系列文章中已经做过详细介绍,有兴趣的同学可以瞧瞧。这里只是简单说一下,它主要用来生成RRO(Runtime Resources Overlay)需要的关键信息,也就是idmap文件。
            调用关系如下图:
    Android资源管理框架-------之总述(一)_第1张图片
            上面的这些文件,是Android资源管理最核心的代码所在。另外,frameworks/base/libs/androidfw/Asset.cppframeworks/base/libs/androidfw/AssetDir.cpp framewrok/base/core/java/android/content/res/TypedArray.javaframework/base/core/java/android/util/TypedValue.javaframeworks/base/core/java/android/app/ResourcesManager.java等要么是一些工具类,要么是对Resources.java等的封装,这里就不作为核心文件介绍了。

组织形式

        这个要从三个方面说,它们分别是:resources.arsc、R.java、以及资源信息加载到内存后的组织形式。

  • resources.arsc     通俗的叫法是资源索引表,它是在aapt编译android资源的时候生成的,用来存储一部分资源以及一些资源相关的信息。这个怎么理解呢,对于raw、图片以及xml类型的资源resources.arsc里会存储它们的路径信息,而对于values类型的资源,比如string、style、integer、dimen、attr、color、array等等,它们会被直接编译到resources.arsc文件中。另外,resources.arsc中还会记录Dynamic Reference信息,用来处理资源共享库。resources.arsc中资源的管理分三级:Package、Type、Entry(MapEntry也是Entry就不再单独列出了)。另外,ResStringPool也是不得不提的一个数据结构,一般一个APK的resources.arsc中会有三个string pool:global string pool、type string pool和key string pool。global string pool也叫value string pool,它主要用来存储string资源中的string、xml和raw以及图片资源的途径等等;type string pool主要用来存储我们的资源的类型字符串的,比如attr、style、integer、dimen等等;key string pool主要用来存储我们的资源项的名称的。这里举个例子就很清楚了:

<resources>
    <string name="app_say_hello">Hellostring>
resources>

<resources>
    <string name="app_say_hello">你好string>
resources>

        假设我们在values和values-zh-rCN目录分别给一个字符串资源app_say_hello赋了英文和中文的值。那么在编译后生成的resources.arsc中,"Hello"和"你好"这两个字符串会被放到 global string pool中;资源的类型字符串"string"会被放到type string pool中;"app_say_hello"这个表示我们资源的名字的字符串会被放到key string pool中。
        介绍resources.arsc的文章,网上有很多,建议大家先去看看,对它有个总体的了解。毕竟Android对资源的管理,核心就是对resources.arsc的加载和处理。看情况吧,如果有时间,我们也会单开一篇文章来详细介绍一下resources.arsc。

  • R.java     也是在aapt编译资源的时候生成的,它的本质就是resources.arsc中的部分资源的索引,也就是说,我们通过R文件来访问资源,是基于索引的,所以速度会很快,当然我们也可以基于文件名来访问,那效率就低多了,所以Android并不推荐这么做。我们知道R.java文件中的字段都是int类型的,它会对应一项具体的资源(更准确地说是一个Res_entry)。R.java中的资源索引值的每个字节的含义大家应该都知道,这里再啰嗦一下哈:第一个字节表示包id,通常我们的一般应用都是0x7f,android系统资源包的id是0x01,mtk的系统资源包的id是0x08,通常各个手机厂商也会加入自己的系统资源包,具体值可能会有所不同,0x00表示资源共享库(关于资源共享库的概念和详细介绍请移步Android资源管理中的SharedLibrary和Dynamic Reference-------之资源共享库(一));第二个字节表示type,比如这个资源是style、drawable还是attr;第三个和第四个字节在一起表示资源在该type中的索引。由于R.java和resources.arsc都是aapt在编译资源的时候生成的,所以它们资源的组织形式都是Package、Type、Entry,是一致的。
  • 资源信息加载到内存后的组织形式     在一个APP起来后,它会去加载这个apk本身的资源信息(系统资源信息zygote已经预先加载过了)。注意我们说的是资源信息,而不一定是资源本身,这里的资源信息就是指resources.arsc。当resources.arsc被加载后,Android资源管理框架为了方便对资源的管理,也创建了一系列的数据结构,来描述和管理这些信息,它们大部分被定义在frameworks/base/include/androidfw/ResourceTypes.h中,一小部分被定义在frameworks/base/lib/androidfw/ResourceTypes.cpp中,它们主要是:ResTableResTable::PackageGroupResTable::PackageResTable::TypeListResTable::TypeResTable::Entry以及ResStringPoolRes_value。相比Package、Type、Entry三级数据组织结构来说复杂了不少,主要是因为Package、Type、Entry用于描述单个资源包尚可,但资源包比较多的时候,就有些力不从心了。我们知道,一个APK运行起来后至少要加载Android系统资源包framework-res.apk以及它本身这个APK中的资源(绝大多数手机还会加载SOC厂商以及手机厂商的资源包,如果有资源共享库和RRO包,要加载的资源包会更多),这样要完整地描述一个应用,三级数据结构显然不够。一个ResTable中可以放多个ResTable::PackageGroup,一个ResTable::PackageGroup中可以放多个ResTable::Package。这里大家可能不太理解,一个ResTable中直接放多个ResTable::Package不就可以了,为啥还要有ResTable::PackageGroup呢? 其实,ResTable::PackageGroupde的存在主要是为了RRO,overlay package会和target package放到同一个ResTable::PackageGroup中,另外ResTable::TypeList的存在也同样是为了RRO,overlay package和target package中相同类型的资源会被放到同一个ResTable::TypeList中。另外,Res_value表示一项资源的值,不过这个值不一定是具体的值,它可以是另外一个资源的索引或者叫作另外一个资源的引用,比如:
<resources>
    <color name="app_white">@android:color/whitestring>
    <color name="app_green">#00FF00string>
resources>

        app_white的值就是另外一个资源android:color/white的引用,而app_green的值则是具体的颜色值#00FF00

资源的配置信息

        资源的配置信息会影响我们在获取资源时获取的结果,比如在不同的语言环境,不同的dpi,不同的屏幕方向下,获取到的资源就有可能不同。配置信息在java层封装在framework/base/core/java/android/content/res/Configuration.java中,在native层封装在framework/base/include/androidfw/ResourceTypes.h的ResTable_config结构体中。当system_server起来后,会去创建并向service_manager注册ActivityManagerService,在ActivityManagerService构造的时候,也会创建它的成员mConfiguration,并给它配置默认值,当然随着系统的运行,mConfiguration也会随着配置的不断改变而同步更新。当system_server启动一个应用的时候(确切地说是在bindApplication的时候),会把这个配置信息传给应用,应用会把它设置到ResourcesAssetManager中,当然最终是设置到native层的AssetManagerResTable中。当然,更新的时候也会这样从上到下进行更新,不过更新的方式有两类:一类是用户通过对应的系统API去主动更新,还有就是通过Sensor去更新,典型的比如横竖屏切换。至于Android是怎么根据不同的config来组织和获取资源,这是Android资源管理的核心,我们后面会详细介绍,这里就不再多说。

        接下来打算从Android的资源包的管理、resources.arsc、资源管理的基本数据结构、TypedArray与TypedValue、bag资源、资源配置信息、资源信息的获取、资源的加载和创建等方面详细介绍Android的资源管理框架。

你可能感兴趣的:(AssetManager)