Reference
需求
- 替换 res 目录中的资源;
- 不重启;
- 无闪烁。
换肤原理
1、设置 Activity 的 LayoutInflater 为自定义的 Factory2,这样当调用 Activity#setContentView() 方法时会接管创建 View 的操作;
2、在创建 View 成功后,查看 View 是否包含在换肤范围内的属性,如果有,就将 View 存起来;
3、当执行换肤时,会先下载皮肤包,然后将皮肤包中的资源合并到宿主 app 的资源中,形成新的资源包;遍历(2)中保存的 View,将资源替换为新的资源包中的资源。
源码:skin_demo
资源
分类
assets
res
- animator:属性动画 XML 文件
- anim:视图动画 XML 文件
- color:颜色 XML 文件
- drawable:drawable XML 文件、Bitmap 文件(.png、.9.png、.jpg、.gif 等)
- layout:布局 XML 文件
- menu:菜单 XML 文件
- raw:原始资源文件
- values:字符串、颜色、尺寸、样式、属性等 XML 文件
- xml:XML 文件
资源打包流程
资源打包是由 AAPT 工具完成,assert、res/raw、Bitmap 文件会被原封不动地打包到 APK 里,XML 会被编译成二进制文件,最终生成 R.java 和 resources.arsc。
详细流程:
R.java 存储资源 ID(系统资源以 0x01 开头,应用资源以 0x7f 开头):
resources.arsc 存储资源 ID 和文件的映射关系:
布局 XML 解析流程
在 Activity 中加载布局 XML 文件
|
|
Activity:直接交给 Window 处理
此处的 Window 是 PhoneWindow。
PhoneWindow:创建 DecorView
创建 DecorView 作为 Window 的顶层 View,然后交给 LayoutInflater 处理,同时 DecorView 作为 parent。
LayoutInflater 处理
1、获取 Context 绑定的 Resources;
2、创建 XML 资源解析器,解析 XML;
3、通过 createViewFromTag() 方法创建 View。
|
|
所以自定义 Factory2 就可以实现抢在系统之前创建 View。
Resources 初始化流程
在 app 启动流程中,当 zygote fork 出 app 进程后,会在 app 进程执行 ActivityThread#main() 方法。
ActivityThread:除了启动主线程 Looper,还创建了 ContextImpl 对象
|
|
ContextImpl:通过 LoadedApk 获取 Resources,并绑定
|
|
LoadedApk:将获取 Resources 的工作转给 ResourcesManager
|
|
ResourcesManager:创建 ResourcesImpl 中的 AssetManager;创建 ResourcesImpl
|
|
AssetManager:将系统资源和用户资源合并
|
|
资源查找流程
以查找字符串为例,依次经过 Context -> ContextImpl-> Resources -> ResourcesImpl -> AssetManager,其中 ContextImpl、ResourcesImpl、AssetManager 在 app 启动时初始化 Resource 时创建。
AssetManager 调用 native 层代码会获取 ResTable 资源表,然后根据机器配置在 ResTable 的 mPackageGroups 数组中查找到一个最合适的值。详细流程参考Android资源查找分析。
Context
|
|
ContextImpl
|
|
Resources
|
|
ResourcesImpl
|
|
AssetManager
|
|