Android对于资源管理这个模块的折腾从Android-Lollipop开始就从未停止过:Android-Lollipop引入了Runtime Resources Overlay,但是bug比较多,多得根本不能用;Android-Marshmallow算是修正了这些bug,Runtime Resources Overlay终于可以生效了。然而,从Android-Nougat开始,到Android-Oreo,再到Android-Pie,这三个版本又对整个Android资源管理模块进行了彻彻底底的重构。比如,在Android N中引入了aapt2,将Android资源的编译分成了两个过程:编译和链接,并将之前的架构彻底推翻重建,另外,ResourcesManager类和Resources也大变样,功能增强不少;Android O中引入了AssetManager2,将AssetManager中ResTable相关的概念彻底废除,引入了更加直观的ApkAssetsLoadedArscLoadedPackage等概念,并且增加了OverlayManagerService等系统服务。但是在这个版本中,AssetManager2只是增加了相关代码,并未真正启用,真正使用的还是原来的AssetManager;在Android P中,java层引入了ApkAssets相关的接口,AssetManager2真正替换掉了原来的AssetManager;到了android-10,又引入了overlay相关的策略来增加安全控制。至此,Android资源管理模块算是完成了重构。其中AAPT2算是对AAPT的推倒重建,两者之间没有依赖关系,所以我们后面可以不用学习AAPT了,毕竟它只是一个过了时的编译工具(说实话,我还是挺喜欢aapt那种代码风格的,对aapt2的风格却不怎么喜欢);但AssetManager2和AssetManager相比,并非一个单独的模块,它本身就是继承自AssetManager的(可能是为了兼容吧),它的代码仍然和AssetManager的放在一起,它仍然使用了原来AssetManager中的不少数据结构,所以,前面我们介绍了那么多AssetManager相关的东西,还是非常有必要的。AssetManager2的代码,我们基于目前最新的Android 10来分析。

相同点

有了前面AssetManager的基础,AssetManager2理解起来就会容易得多。其实在资源的获取方面,AssetManaager2的思路和流程与AssetManager相比,基本是一致的:根据资源id,分别得到PackageId、typeIndex、entryIndex;根据PackageId找到对应PackageGroup的索引,进而得到对应的PackageGroup;从对应的PackageGroup中根据typeIndex拿到该PackageGroup中所有包该类型的资源;遍历这些资源,根据entryIndex,从中选取最符合设备当前配置的entry;如果有资源共享库,还需要查一下DynamicReferenceTable,将动态引用转化为静态引用;如果有必要,还要把得到的静态引用解析一下,大概就是这么个流程。

不同点

然后,我们重点说说AssetManager2的不同吧。首先就是代码风格了,这个我适应了很久。按道理说,同一个模块的代码,整体风格应该保持一致吧。但看看AssetManager2,再看看AssetManager,我还以为我看错了,感觉就像一个人上半身西装领带,下半身拖鞋裤衩一样,违和感太强了,哈哈。

我们知道AssetManager在native层,有大量的代码放在ResourceTypes.cpp(以及ResourceTypes.h)中,AssetManager2则对这部分代码做了整理,从中抽取出了部分代码单独存放,这样结构显得清晰了一些。总体来讲,AssetManager的一些关键类比较庞大,而AssetManager2中则对它们做了拆分。

当然,最重要的在于,AssetManager2的整体架构和AssetManager完全不一样,资源的组织方式也完全不同。在AssetManager中,资源的核心处理逻辑都是放在ResTable这个类中的;而在AssetManager2中,则完全废弃了这个类,而是把资源处理的核心逻辑直接放到了AssetManager2这个类中。同时引入了ApkAssetsLoadedArscLoadedPackage等类,来分担原来ResTable的功能。特别是ApkAssets概念的引入,它会直接影响到应用层的API。比如,Android P以及以后的版本中AssetManager类增加了Builder类,应用层也新增了ApkAssets类,用来创建AssetManager对象。也就是说,ApkAssets的概念是从应用到底层贯穿整个资源管理模块的。

新增主要文件

frameworks/base/libs/androidfw/include/androidfw/AssetManager2.h
frameworks/base/libs/androidfw/AssetManager2.cpp
        AssetManager2类是AssetManager的子类,可以认为相当于之前的AssetManager + ResTable,由于没有了ResTable类,所以AssetManager2做了部分ResTable的工作。

frameworks/base/core/java/android/content/res/ApkAssets.java
frameworks/base/libs/androidfw/include/androidfw/ApkAssets.h
frameworks/base/libs/androidfw/ApkAssets.cpp
        一个ApkAssets对应于一个APK包,ApkAssets关注的重点在于这个包里的resources.arsc以及这个APK包对应的idmap文件(如果有的话)。另外,ApkAssets也是AssetManager2中非常重要的概念,之前版本的资源管理模块中没有数据结构与之对应。

frameworks/base/libs/androidfw/include/androidfw/LoadedArsc.h
frameworks/base/libs/androidfw/LoadedArsc.cpp
        这两个文件中定义了两个非常重要的类LoadedArscLoadedPackage。一个LoadedArsc对应于一个APK中的resources.arsc文件。我们知道一个resources.arsc中只有两种数据结构,一个是Global String Pool,一个是Package,LoadedArsc的内容也是如此。一个LoadedPackage则对应于一个LoadedArsc中的(或者说resources.arsc中的)Package,我们可以简单认为它相当于之前版本的资源管理模块中的ResTable::Package

frameworks/base/libs/androidfw/include/androidfw/Idmap.h
frameworks/base/libs/androidfw/Idmap.cpp
        从之前的ResTable中分拆出来的idmap文件解析相关的逻辑被放到了这两个文件中。

frameworks/base/libs/androidfw/include/androidfw/AttributeFinder.h
frameworks/base/libs/androidfw/include/androidfw/AttributeResolution.h
frameworks/base/libs/androidfw/AttributeResolution.cpp
        Theme和style相关,从之前的Theme相关模块中分离出来的,主要用于属性的查找和解析。

关键数据结构

// framework/base/libs/androidfw/include/androidfw/AssetManager2.h
class AssetManager2 {//...... 省略非关键代码//用来存储该AssetManager2已经加载的所有APK包std::vector<const ApkAssets*> apk_assets_;/*** 用来将apk_assets_分组,概念和之前的ResTable::PackageGroup一样* 主要还是用来处理Runtime Resources Overlay的* 但它内部的结构已经完全和ResTable::PackageGroup不一样了*/std::vector<PackageGroup> package_groups_;/*** 叫package_ids_很容易让人费解,感觉还是叫package_map比较合适* 因为它和ResTable::mPackageMap的作用一模一样* 它的key表示APK包也就是ApkAssets的id,比如应用是0x7f,系统是0x01等* 它的value表示PK包也就是ApkAssets所在的PackageGroup在package_groups_中的索引*/std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;//表示设备当前的配置信息,相当于ResTable::mParamsResTable_config configuration_;/*** 相当于ResTable::PackageGroup::bags,用来缓存资源的Bag* 它的key表示一个资源的id,比如一个style,一个array* 它的value 表示已经从resources.arsc中解析出来了的,该资源的所有Bag*/std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;/*** 我们知道当我们获取资源的时候,系统需要选取最符合当前配置的资源* 这个过程包括match、isBetterThan、isMore,isMoreSpecificThan等比较过程* 这个变量决定是否记录这些过程*/bool resource_resolution_logging_enabled_ = false;//当resource_resolution_logging_enabled_ == true时,用来记录这个过程用的mutable Resolution last_resolution;
}

其中最核心的是apk_assets_package_groups_package_ids_configuration_这几个成员最为核心,它们在一定程度上代表了AssetManager2中资源的组织方式,资源的加载、查找等过程的实现,也必须依赖这几个成员。我们看到Bag资源的缓存也从ResTable::PackageGroup类被移动到了AssetManager2类。我们先来看看代表一个APK包的ApkAssets

// framework/base/libs/androidfw/include/androidfw/ApkAssets.h
class ApkAssets {//...... 省略非关键代码//一个智能指针,表示一个压缩包,也就是我们的APKZipArchivePtr zip_handle_;//我们的apk的路径const std::string path_;//APK的最近一次修改时间time_t last_mod_time_;//一个智能指针,表示APK中resources.arscstd::unique_ptr<Asset> resources_asset_;//一个智能指针,表示一个idmap文件std::unique_ptr<Asset> idmap_asset_;/*** 一个智能指针,表示一个resources.arsc* 和resources_asset_的区别在于,resources_asset_是表示APK中的resources.arsc文件* 而loaded_arsc_则是对这个文件加载解析后的一个数据结构*/std::unique_ptr<const LoadedArsc> loaded_arsc_;
}

一个ApkAssets对象包括了一个APK的全部信息,包括动态的(load到内存后的,比如loaded_arsc_)和静态的(文件本身的,比如代表resources.arsc以及idmap文件的Asset)。这也是AssetManager2的一种思想吧,就是所有一个类会包括与之相关的全部信息;而原来的AssetManager则是分开的,动态的信息全部放到ResTable中,静态的信息全部放到ZipSetSharedZip等数据结构中。idmap文件虽然没有打包在overlay package中,但对于一个overlay package而言,它也是这个包本身的重要部分,所以也就一起记录在ApkAssets对象中了。当然,ApkAssets的概念在java层也同样存在,并且它还是Android Framework API的一部分,当然它只是一个空壳,具体实现还在native层,java层的ApkAssets我们那再以后会说,这里就不啰嗦那么多了,我们接下来看看它的重要成员LoadedArsc

// framework/base/libs/androidfw/include/androidfw/LoadedArsc.h
class LoadedArsc {//...... 省略非关键代码//Global String PoolResStringPool global_string_pool_;//packages,对应resources.arsc中的类型为RES_TABLE_PACKAGE_TYPE的chunkstd::vector<std::unique_ptr<const LoadedPackage>> packages_;//是否是系统资源bool system_ = false;
}

LoadedArsc非常简单,主要就是一个Global String Pool和多个Package,这和resources.arsc中的内容是一致的。需要说明的是,我们看到从Android Lollipop版本(更早的没确认过)开始,系统就是支持一个resources.arsc中存在多个Package的,但是到现在也并没有哪个应用或者某个系统模块使用了这一特性。至少我还没有见到过哪个resources.arsc是1个Global String Pool + N个package的模式,大家都是一个1个Global String Pool + 1个package。所以,这个特性还是有待充分开发的。当然,难度比较大,因为它涉及到了打包的具体细节,可能还要修改aapt2。

// framework/base/libs/androidfw/include/androidfw/LoadedArsc.h
class LoadedPackage {//...... 省略非关键代码//Type String PoolResStringPool type_string_pool_;//key String PoolResStringPool key_string_pool_;//package name std::string package_name_;//pacakge idint package_id_ = -1;/*** type Id的偏移量,需要说明的是* type Id是从1开始的,如果type_id_offset_的值为2* 那么type Id将会从3开始*/ int type_id_offset_ = 0;//该package是否为资源共享库bool dynamic_ = false;//该package是否为系统资源库bool system_ = false;//该package是否为overlay pacakgebool overlay_ = false;//该pacakge是否定义了overlay policy信息bool defines_overlayable_ = false;/*** 对应于resources.arsc中的TypeSpec* using TypeSpecPtr = util::unique_cptr<TypeSpec>;*/ByteBucketArray<TypeSpecPtr> type_specs_;//仅作遍历时时使用,可以无视ByteBucketArray<uint32_t> resource_ids_;/*** 这个package所引用的资源共享库* DynamicPackageEntry就两个成员* 一个是资源共享库的name,* 一个是资源共享库的id,需要注意的是,这个id是本package编译时* 给资源共享库临时分配的id,在运行时由于加载顺序的关系,同一个资源共享库的id可能会* 和编译时不一样,所以这中间要有一个转换的过程,这就是DynamicReferenceTable的主要作用*/std::vector<DynamicPackageEntry> dynamic_package_map_;//后面的这两个成员都和RRO策略有关,到时候我们会详细说明,这里可以不用太在意/*** vector中的每一个元素表示一种策略,比如这个package允许相同签名的overlay package* 覆盖的所有资源算是一个策略;又比如这个package允许/product分区的overlay package* 覆盖的所有资源又是一个策略。std::unordered_set<uint32_t>表是这个策略允许覆盖的所有* 资源的id*/std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;std::unordered_map<std::string, std::string> overlayable_map_;
}

AssetManager2中还有一个非常重要的结构:AssetManager2::PackageGroup,它的概念和ResTable::PackageGroup相同,都是为了存储RRO中的target package和overlay package而提出的。不过,它的实现和以前已经有了不同:

// framework/base/libs/androidfw/include/androidfw/AssetManager2.h/*** 这个结构体可以认为是一个缓存,为了加快获取资源的速度* 代表一个ResTable_typeSpec中,符合设备当前配置的所有ResTable_type* 这里的符合表示ResTable_config的match方法返回true*/
struct FilteredConfigGroup {//该ResTable_typeSpec中符合设备当前配置的所有的configstd::vector<ResTable_config> configurations;///该ResTable_typeSpec中符合设备当前配置的所有的ResTable_typestd::vector<const ResTable_type*> types;
};/*** 这个结构体我们可以简单理解为LoadedPackage + 缓存*/
struct ConfiguredPackage {// A pointer to the immutable, loaded package info.const LoadedPackage* loaded_package_;// A mutable AssetManager-specific list of configurations that match the AssetManager's// current configuration. This is used as an optimization to avoid checking every single// candidate configuration when looking up resources./*** 我们在获取资源的时候,要根据设备的当前配置信息,去选择最合适的资源项* 这个过程我们之前讲过,要经过match、isBetterThan、isMoreSpecificThan* 等比较的过程。现在为了加快获取资源的速度,在加载完资源后,系统就会先选出match* 设备当前配置的资源,存放在filtered_configs_中。当我们获取资源的时候,就可以* 跳过这个步骤了。filtered_configs_中的每一项代表一个ResTable_typeSpec中* 符合设备当前配置的所有ResTable_type*/ByteBucketArray<FilteredConfigGroup> filtered_configs_;
}//代表一个PackageGroup
struct PackageGroup {// The set of packages that make-up this group./*** PackageGroup中的所有package(我们可以简单认为就是LoadedPackage对象)* 换句话说就是target package 和它的所有overlay package* 如果一个Package没有overlay package,那么它应该独占一个PackageGroup*/std::vector<ConfiguredPackage> packages_;// The cookies associated with each package in the group. They share the same order as// packages_./*** ApkAssetsCookie就是一个32位的int值* cookies 表示PackageGroup中的所有package(我们可以简单认为就是LoadedPackage对象)* 所在的ApkAssets对象在AssetManager2::apk_assets_中的索引* 这里需要注意的是,一个ApkAssets对象中是有可能包含多个LoadedPackage对象的*/std::vector<ApkAssetsCookie> cookies_;// A library reference table that contains build-package ID to runtime-package ID mappings.//DynamicRefTable,就是ResTable::DynamicRefTable.用来保存//资源共享库的编译时id和运行时id的映射关系DynamicRefTable dynamic_ref_table;
};

我们看到AssetManager2::PackageGroupResTable::PackageGroup相比,概念还是原来的概念,不过实现上有了一些变动:把对Bag资源的缓存移动到了AssetManager2::cached_bags_中,另外为了加快资源的查找速度,AssetManager2::PackageGroup还会提前缓存好符合设备当前配置的资源信息。另外,如果一个resources.arsc中包含了多个package,也就是一个LoadedArsc中包含了多个LoadedPackage的话,那么这些LoadedPackage虽然不一定会被分到同一个AssetManager2::PackageGroup中,但它们的cookie值会是相同的,毕竟它们来自同一个资源包。可能这句话不太好理解,不过没关系,在后面的文章中我们会详细解释的。最后我们看一下AssetManager2中主要的数据结构和resources.arsc中主要数据结构的对应关系:

        图片比较大,建议点击一下,放大了看,这样比较清晰。我们对这个图稍作说明:

  • 图中最右面的那个表示一个资源包(我们可以简单理解为一个APK啦,dex什么的请无视)中的主要数据结构
  • 剩下的数据结构都是AssetManager2中为了方便资源管理而定义的
  • PackageGroup::cookiesAssetManager2::configuration_ApkAssets::idmap_asset_三个变量我们没有画出其内部的数据结构图。其中PackageGroup::cookiesAssetManager2::configuration_一个表示资源包的索引值,一个表示设备当前的配置信息,很好理解,我们不再详说;ApkAssets::idmap_asset_对应这个资源包的idmap文件(这个时候这个资源包是一个overlay package,idmap文件位于/data/resource-cache/目录下),我们后面介绍RRO的时候会详说,这里也不再详述。
  • 一个AssetManager2中包含多个ApkAssets
  • 一个ApkAssets对应一个资源包(也就是APK啦),并且它内部会有一个LoadedArsc
  • 一个LoadedArsc对应一个资源包中的resources.arsc文件,它内部会有一个Global String Pool,对应于resources.arsc中的Global String Pool;它内部还会有一个或者多个LoadedPackage
  • 一个LoadedPackage对应于resources.arsc中的一个ResTable_package,它内部会有一个Type String Pool、一个Key String Pool、N个typeSpec、一个记录其引用的资源共享库的数据结构(dynamic_package_map_)、一些RRO策略相关的数据结构(overlayable_infos_等),它们都对应于resources.arsc中相应的数据段。
  • 一个AssetManager2中还会包含多个PackageGroup对象。PackageGroup在resources.arsc中并没有数据结构与其对应,它是Android为了方便管理RRO中的target package 和它所有overlay packages而定义的一个结构体。target package 和它所有overlay packages会被放到同一个PackageGroup中,它里面会有多个ConfiguredPackage,而每一个ConfiguredPackage中都会有一个LoadedPackage对象作为成员。也就是说,PackageGroupLoadedPackage除了LoadedArsc–>ApkAssets外的另外一种组织形式,如图中的红色虚线箭头。我们可以认为图中的橙色箭头+黑色箭头是一种组织形式;红色虚箭头+ 黑箭头是另外一种组织形式。当然,PackageGroup中还会记录target package引用的资源共享库的编译时索引和运行时索引之间的映射关系,也就是PackageGroup::dynamic_ref_table。它的生成需要依赖target package中的dynamic_package_map_,自然也就会依赖resources.arsc中的ResTable_lib_header等数据结构。

Android资源管理框架-------之AssetManager2总述(一)相关推荐

  1. Android 资源管理框架(Resources和AssetManager) 及 资源编译(aapt)

    android应用资源预编译,编译和打包全解析- https://cloud.tencent.com/developer/article/1033926   Android系统提供了一套强大的资源管理 ...

  2. Android资源管理框架(Asset Manager)简要介绍和学习计划

    Android应用程序主要由两部分内容组成:代码和资源.资源主要就是指那些与UI相关的东西,例如UI布局.字符串和图片等.代码和资源分开可以使得应用程序在运行时根据实际需要来组织UI.这样就可使得应用 ...

  3. Android资源管理框架(二)AssetManager创建过程

     Android应用程序在运行的过程中,是通过一个称为AssetManager的资源管理器来读取打包在APK文件里面的资源文件的.在本文中,我们就将详细分析Android应用程序资源管理器的创建以 ...

  4. 美团Android资源混淆保护实践

    前言 Android应用中的APK安全性一直遭人诟病,市面上充斥着各种被破解或者汉化的应用,破解者可以非常简单的通过破解工具就能对一个APK进行反编译.破解.汉化等等,这样就可以修改原有代码的逻辑.添 ...

  5. android虚拟手机云之一:总述

    年前福哥离开了,今天和强哥交接了下代码.强哥从头介绍了下项目,挺伤感的.我也记一下强哥的介绍,加上我自己的理解,理清下整个虚拟手机云项目.下面是给我自己的笔记.希望以后在改到android框架的时候能 ...

  6. 关于大型网站技术演进的思考(九)--网站静态化处理--总述(1)

    在存储瓶颈的 开篇我提到像hao123这样的导航网站只要它部署的web服务器数量足够,它可以承载超大规模的并发访问量,如果是一个动态的网站,特别是使用到了数据 库的网站是很难做到通过增加web服务器数 ...

  7. LSMW批处理使用方法(01)_总述及界面说明

    一.总述 在SAP系统中,批处理操作有多种方法.如果是对一个事物码(TCODE)进行批处理操作,常用的是LSMW.LSMW全称是Legacy System Migration Workbench.它能 ...

  8. Hadoop——分布式资源管理框架YARN总结

    分布式资源管理框架YARN 1. YARN概述   YARN是"Yet Another Resource Negotiator"的简称.   在进一步了解 YARN 框架之前我们需 ...

  9. 【重识云原生】第二章计算第一节——计算虚拟化技术总述

    云平台计算领域知识地图: ​ 楔子:计算虚拟化技术算是云计算技术的擎天之柱,其前两代技术的演进一直引领着云计算的发展,即便到了云原生时代,其作用依然举足轻重. 一.计算虚拟化技术总述 1.1 虚拟化技 ...

最新文章

  1. 视觉SLAM直接法与特征法及其在多传感融合中的思考
  2. word转pdf图片模糊怎么办_迅捷PDF转换器如何将word转为长图?word转图片方法
  3. CloudStack管理员文档 - 服务方案
  4. SpringBoot_web开发-thymeleaf语法
  5. C# 重写WndProc 消息循环
  6. java 反射 new class_Java高级特性-反射:不写死在代码,还怎么 new 对象?
  7. 奇店社群社区团购小程序v5.5.9
  8. 使用python排序_Python排序
  9. 2021-2025年中国云企业管理软件行业市场供需与战略研究报告
  10. java取负数_阿里巴巴 Java 开发手册之MySQL 规约
  11. FTP服务器windows配置
  12. MySQL基础实战篇
  13. Atitit各种驱动的xdd tdd bdd设计 ATDD ddd v3 u66.docx Atitit各种驱动的xdd tdd bdd设计 ATDD ddd v2 s66 开发方法论与效率提
  14. 海康设备搜索器搜索协议解析
  15. 计算机网络有哪三种地址,IP地址的分类有哪几种
  16. redistemplate opsforvalue和boundValueOps
  17. Java中使用JCOM操作Word/Excel对象
  18. 广东开放大学成本会计
  19. c语言证自考免计算机应用,自考证书有哪些?可以免考什么课程?
  20. sqlserver drop image类型的列不释放空间问题处理

热门文章

  1. 全能测试库Go Monkey,已支持arm64,还有了这些功能增强
  2. 如何通过python实现一个web自动化测试框架?
  3. win10固态硬盘分区 整数_戴尔Vostro 5390重装win10系统,以及BIOS如何设置
  4. AI教程如何在 Illustrator2022 中编辑画板?
  5. CarSim和Simulink联合仿真-无人驾驶
  6. 谷歌神秘项目曝光!能写代码还会改bug的AI,让码农瑟瑟发抖
  7. 【C51单片机】抽号摇奖机设计(仿真)
  8. Z3Py教程(翻译)
  9. 《直播从零开始》SRS 安装与部署RTMP服务
  10. 实现全排列算法(C++)