Android资源管理框架-------之AssetManager2总述(一)
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
相关的概念彻底废除,引入了更加直观的ApkAssets
、LoadedArsc
、LoadedPackage
等概念,并且增加了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
这个类中。同时引入了ApkAssets
、LoadedArsc
、LoadedPackage
等类,来分担原来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
这两个文件中定义了两个非常重要的类LoadedArsc
和LoadedPackage
。一个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
中,静态的信息全部放到ZipSet
、SharedZip
等数据结构中。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::PackageGroup
和ResTable::PackageGroup
相比,概念还是原来的概念,不过实现上有了一些变动:把对Bag资源的缓存移动到了AssetManager2::cached_bags_
中,另外为了加快资源的查找速度,AssetManager2::PackageGroup
还会提前缓存好符合设备当前配置的资源信息。另外,如果一个resources.arsc中包含了多个package,也就是一个LoadedArsc
中包含了多个LoadedPackage
的话,那么这些LoadedPackage
虽然不一定会被分到同一个AssetManager2::PackageGroup
中,但它们的cookie值会是相同的,毕竟它们来自同一个资源包。可能这句话不太好理解,不过没关系,在后面的文章中我们会详细解释的。最后我们看一下AssetManager2中主要的数据结构和resources.arsc中主要数据结构的对应关系:
图片比较大,建议点击一下,放大了看,这样比较清晰。我们对这个图稍作说明:
- 图中最右面的那个表示一个资源包(我们可以简单理解为一个APK啦,dex什么的请无视)中的主要数据结构
- 剩下的数据结构都是AssetManager2中为了方便资源管理而定义的
PackageGroup::cookies
、AssetManager2::configuration_
、ApkAssets::idmap_asset_
三个变量我们没有画出其内部的数据结构图。其中PackageGroup::cookies
和AssetManager2::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
对象作为成员。也就是说,PackageGroup
是LoadedPackage
除了LoadedArsc
–>ApkAssets
外的另外一种组织形式,如图中的红色虚线箭头。我们可以认为图中的橙色箭头+黑色箭头是一种组织形式;红色虚箭头+ 黑箭头是另外一种组织形式。当然,PackageGroup
中还会记录target package引用的资源共享库的编译时索引和运行时索引之间的映射关系,也就是PackageGroup::dynamic_ref_table
。它的生成需要依赖target package中的dynamic_package_map_
,自然也就会依赖resources.arsc中的ResTable_lib_header
等数据结构。
Android资源管理框架-------之AssetManager2总述(一)相关推荐
- Android 资源管理框架(Resources和AssetManager) 及 资源编译(aapt)
android应用资源预编译,编译和打包全解析- https://cloud.tencent.com/developer/article/1033926 Android系统提供了一套强大的资源管理 ...
- Android资源管理框架(Asset Manager)简要介绍和学习计划
Android应用程序主要由两部分内容组成:代码和资源.资源主要就是指那些与UI相关的东西,例如UI布局.字符串和图片等.代码和资源分开可以使得应用程序在运行时根据实际需要来组织UI.这样就可使得应用 ...
- Android资源管理框架(二)AssetManager创建过程
Android应用程序在运行的过程中,是通过一个称为AssetManager的资源管理器来读取打包在APK文件里面的资源文件的.在本文中,我们就将详细分析Android应用程序资源管理器的创建以 ...
- 美团Android资源混淆保护实践
前言 Android应用中的APK安全性一直遭人诟病,市面上充斥着各种被破解或者汉化的应用,破解者可以非常简单的通过破解工具就能对一个APK进行反编译.破解.汉化等等,这样就可以修改原有代码的逻辑.添 ...
- android虚拟手机云之一:总述
年前福哥离开了,今天和强哥交接了下代码.强哥从头介绍了下项目,挺伤感的.我也记一下强哥的介绍,加上我自己的理解,理清下整个虚拟手机云项目.下面是给我自己的笔记.希望以后在改到android框架的时候能 ...
- 关于大型网站技术演进的思考(九)--网站静态化处理--总述(1)
在存储瓶颈的 开篇我提到像hao123这样的导航网站只要它部署的web服务器数量足够,它可以承载超大规模的并发访问量,如果是一个动态的网站,特别是使用到了数据 库的网站是很难做到通过增加web服务器数 ...
- LSMW批处理使用方法(01)_总述及界面说明
一.总述 在SAP系统中,批处理操作有多种方法.如果是对一个事物码(TCODE)进行批处理操作,常用的是LSMW.LSMW全称是Legacy System Migration Workbench.它能 ...
- Hadoop——分布式资源管理框架YARN总结
分布式资源管理框架YARN 1. YARN概述 YARN是"Yet Another Resource Negotiator"的简称. 在进一步了解 YARN 框架之前我们需 ...
- 【重识云原生】第二章计算第一节——计算虚拟化技术总述
云平台计算领域知识地图: 楔子:计算虚拟化技术算是云计算技术的擎天之柱,其前两代技术的演进一直引领着云计算的发展,即便到了云原生时代,其作用依然举足轻重. 一.计算虚拟化技术总述 1.1 虚拟化技 ...
最新文章
- 视觉SLAM直接法与特征法及其在多传感融合中的思考
- word转pdf图片模糊怎么办_迅捷PDF转换器如何将word转为长图?word转图片方法
- CloudStack管理员文档 - 服务方案
- SpringBoot_web开发-thymeleaf语法
- C# 重写WndProc 消息循环
- java 反射 new class_Java高级特性-反射:不写死在代码,还怎么 new 对象?
- 奇店社群社区团购小程序v5.5.9
- 使用python排序_Python排序
- 2021-2025年中国云企业管理软件行业市场供需与战略研究报告
- java取负数_阿里巴巴 Java 开发手册之MySQL 规约
- FTP服务器windows配置
- MySQL基础实战篇
- Atitit各种驱动的xdd tdd bdd设计 ATDD ddd v3 u66.docx Atitit各种驱动的xdd tdd bdd设计 ATDD ddd v2 s66 开发方法论与效率提
- 海康设备搜索器搜索协议解析
- 计算机网络有哪三种地址,IP地址的分类有哪几种
- redistemplate opsforvalue和boundValueOps
- Java中使用JCOM操作Word/Excel对象
- 广东开放大学成本会计
- c语言证自考免计算机应用,自考证书有哪些?可以免考什么课程?
- sqlserver drop image类型的列不释放空间问题处理
热门文章
- 全能测试库Go Monkey,已支持arm64,还有了这些功能增强
- 如何通过python实现一个web自动化测试框架?
- win10固态硬盘分区 整数_戴尔Vostro 5390重装win10系统,以及BIOS如何设置
- AI教程如何在 Illustrator2022 中编辑画板?
- CarSim和Simulink联合仿真-无人驾驶
- 谷歌神秘项目曝光!能写代码还会改bug的AI,让码农瑟瑟发抖
- 【C51单片机】抽号摇奖机设计(仿真)
- Z3Py教程(翻译)
- 《直播从零开始》SRS 安装与部署RTMP服务
- 实现全排列算法(C++)