理解Android资源索引

今天来说说Android的资源索引系统, Android的资源编译使用aapt工具, 查找则使用AssertManager.

Android如何来索引资源呢,首先我们从一个应用程序来找资源进行分析.
Android中通过id来进行资源的查找, 那么怎么通过id来查找呢

我们对id进行分割, id是一个32位的数字, 前8位为packageid, 后面8为为typeid,最后16位是在该type下的资源索引.

首先根据packageid找到资源索引文件,也就是resources.arsc文件. 根据在根据type找到对应的type集合, type类型一般有drawable, layout, anim等等, 再根据id的后16为找到对应id描述的资源, 最后根据当前系统配置,选择最合适的资源,

Aapt

AaptDir: 描述一个类型的文件夹,比如 drawable,drawable-xxxdpi都对应drawler AaptDir, Aaptdir中存放多个AaptGroup
AaptGroup: 则描述AaptDir下相同名称的文件, 比如drawable/a.xml drawable-xxxdpi/a.xml, 都放在同一个AaptGroup下, 每个文件对应一个AaptFile

AaptFile: 则具体描述了一个资源文件.

aapt编译的过程首先就是组织文件,也就是描述AaptDir->AaptFile的树状结构, 之后编译文件.也就是对文件名称进行索引.
之后编译values文件夹下的资源生成索引
之后编译xml

剩下的就是写文件,写R.java

编译的过程其实就是将AaptDir->AaptFile转换为
Type
ConfigList
Entry的过程

Type对应AaptDir 描述一类资源如 drawable,drawable-xxxdpi 都属于drawable类型资源,一个type下有多个ConfigList
ConfigList: 描述相同id资源的不同属性资源如 xxdpi xdpi属于不同属性, 索引最终要就是找到ConfigList, 每个ConfigList下的不同属性相同id的资源用Entry描述
Entry:描述一个资源的值以及配置, 系统会根据配置选择ConfigList的Enrty

assets->slurpFromArgs(bundle) 收集文件.
AaptDir 表示一个文件夹
AaptFile对应一个文件, AaptFile 的mGroupEntry成员变量类型为 AaptGroupEntry,代表文件的属性信息,如 drawler-xxhdpi , 属性信息为xxhdpi
AaptGroup 为一组同名不同属性的文件

编译资源步骤
parsePackage 从Manifest解析出包名
ResourceTable.addIncludedResources 添加引用资源 如android.jar
collect_files 收集资源文件
applyFileOverlay 执行overlay
makeFileResources 收集资源项
compileResourceFile 编译values 文件
ResourceTable.assignResourceIds() 分配资源id
        Entry->generateAttributes(this, p->getName()) 给bag资源分配id
        Entry->assignResourceIds(this, p->getName()) 给bag资源分配到的id保存到bagKeyId 中
compileXmlFile 编译xml文件
        XMLNode.assignResourceIds 给xml的attribute分配id
        XMLNode.parseValues 给xml的属性设置value
        XMLNode.flatten 压平
                XMLNode.collect_resid_strings 收集属性字符串,属性id
                XMLNode.collect_strings 收集字符串
                 写xml头, 写字符串池块,写id池块
                XMLNode.flatten_node 写node块
字符串池块

                 XMLNode.flatten_node 压平node

命名空间池
在这里插入图片描述
compileXmlFile 编译AndroidManifest.xml文件
ResourceTable.addSymbols 生成R文件内容
ResourceTable.flatten 生成包级信息
        StringPool valueStrings(useUTF8) 值字符串池,全局
        StringPool typeStrings(useUTF8); 类型字符串池. 包级
        StringPool keyStrings(useUTF8); 资源名字符串池, 包级
        configTypeName = “1complex”; 编译类型
        configTypeName = “2value”; 值类型
        Package.setTypeStrings(typeStrings.createStringBlock()); 收集typestrings
        Package.setKeyStrings(keyStrings.createStringBlock()); 收集keystrings
        1 收集typeStrings, keyStrings ,valueStrings
        2Entry.prepareFlatten(&valueStrings, this,
&configTypeName, &config) 收集keystrings
        3 写RES_TABLE_PACKAGE_TYPE 头文件ResTable_package 每个包对应一个
        4 写typestrings
        5 写keystrings
        6 写依赖库信息RES_TABLE_LIBRARY_TYPE ResTable_lib_header
        7 写RES_TABLE_TYPE_SPEC_TYPE ResTable_typeSpec 用于描述每种type下每个资源支持的配置信息,通过uint32_t类型数类描述支持的配置信息
         8 写RES_TABLE_TYPE_TYPE ResTable_type 用于描述每种type下每种配置下对应的资源项, 也即没重类型的每个配置对应一个ResTable_type. 如下图, 该结构分为三部分,分别是头部, 描述资源具体信息的偏移信息,以及资源的具体信息 ResTable_entry. 所谓ResTable_entry其实保存的就是Res_Value(对于数字类型的Res_value->data为具体的数字, 对于字符串类型的Res_value->data为字符串池中的索引值,文件名其实也是字符串类型, 对于引用类型Res_value->data保存id值), 对于bag类型则保存bag信息. 这里不展开bag
Restable_type
写入RES_TABLE_TYPE ResTable_header 到resources.arsc
写如 valuestrings字符串池
写入ResourceTable.flatten 生成的包级信息
写R.java文件

所以总结一下resources.arsc的格式
ResTable_header
StringPool: valuestrings
    PackageInfo: 0 … package_size
        ResTable_package
        StringPool: typestrings
        StringPool: keystrings
        ResTable_lib_header
            0…type_size
            ResTable_typeSpec       0…config_size
            ResTable_type       0…config_size

attribute_entry 描述xml的一个属性

    struct attribute_entry {
        String16 ns;  xml属性的命名空间
        String16 name;  属性名
        String16 string; // 值字符串
        Res_value value;  // 解析后的值
        uint32_t index; // 第几个属性
        uint32_t nameResId;  属性对应的id  
        mutable uint32_t namePoolIdx; // 在字符串池中的索引
    };

Res_value 描述一个值

struct Res_value
{
    // Number of bytes in this structure.
    uint16_t size;              // 结构提占用的内存大小

    // Always set to 0.
    uint8_t res0;    // 总是设置成0
        
    uint8_t dataType;  数据类型
    
    // The data for this item, as interpreted according to dataType.
    typedef uint32_t data_type;
    data_type data;  // 数据值
};

对于数字类型的Res_value->data为具体的数字, 对于字符串类型的Res_value->data为字符串池中的索引值,文件名其实也是字符串类型, 对于引用类型Res_value->data保存id值

   class Entry{
    private:
        String16 mName;   // 名字
        String16 mParent; // 父名字
        type mType;   // 类型 TYPE_ITEM 和TYPE_BAG 
        Item mItem;  // 当类型为TYPE_ITEM 使用该item存放value
        int32_t mItemFormat;       // entry表示的类型
        KeyedVector<String16, Item> mBag;  // TYPE_BAG 描述该资源下的bag,其实每个bag也是一个Item
        int32_t mNameIndex;     //  字符串池中的索引
        uint32_t mParentId;
        SourcePos mPos;
    };
class Type {
private:
        String16 mName;  //名字
        SourcePos* mFirstPublicSourcePos;
        DefaultKeyedVector<String16, Public> mPublic;
        DefaultKeyedVector<String16, sp<ConfigList> > mConfigs; //资源同名项
        Vector<sp<ConfigList> > mOrderedConfigs; ///排序同名资源项
        SortedVector<String16> mCanAddEntries; 
        int32_t mPublicIndex;
        int32_t mIndex;            //第几项
        SourcePos mPos;
}
class Package {
        const String16 mName;     //包名
        const size_t mPackageId; // id
        DefaultKeyedVector<String16, sp<Type> > mTypes;  // 支持的类型
        Vector<sp<Type> > mOrderedTypes;  // 排序后的类型
        sp<AaptFile> mTypeStringsData;   // 类型字符串池文件
        sp<AaptFile> mKeyStringsData; // 名称字符串池文件
        ResStringPool mTypeStrings;     // 类型字符串池
        ResStringPool mKeyStrings;// 名称字符串池
        DefaultKeyedVector<String16, uint32_t> mTypeStringsMapping;
        DefaultKeyedVector<String16, uint32_t> mKeyStringsMapping;
}

所以resource的编译过程包括两部分, 分别是xml编译和resources.arsc 字符串索引

最重要的数据结构 Res_value , Entry, Item , attribute_entry, StringPool

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值