首先声明一下,该文章分析参考小米MIUI多主题一文中的介绍:Android系统如何实现换肤及MIUI
文档名称:《MIUI主题风格_一种Android系统换肤功能的设计》大家可自行搜索下载。
另外考虑到排版的问题,在分析源码的过程中只会留下关键的点,太长的源码会以“代码段x.x{}”替代省略。但是主要的逻辑我会描述清楚。
1 什么是系统主题?
这一段这样的对话: 领导问我,主题能替换哪些资源啊?
我不加思索的回答:文字,颜色,图片,恩…… 还有大小。
领导说: 还有呢?
我思考了一会儿,内心来了一段对白: 折腾了这么久,就支持这几个? 是不是忽略了哪些地方,但是实在没有想到别的。
我就回答: 是的。
领导走了,剩下我呆呆的看着车载界面,试图从里面找到我忽略的地方?
不知道多久,突然我反应过来, 界面除了文字,颜色,图片,大小,还有别的吗?
恩是的,这就是主题,就是替换系统和应用图片,文字,颜色,大小,使其展示一种新的风格。

2 要实现系统主题应该思考哪些问题呢?
2.1 资源重定向:什么是资源重定向呢?
简单来说就是在应用通过Resources类获取资源的过程中,把应该返回的图片A/颜色A ,替换成主题的 图片B/颜色B。从而实现界面风格的变化。
2.2 即时刷新:什么是即时刷新呢?
在切换主题的时候,设备的当前页面以及之前用户打开过的页面,都要刷新一下,保证这些页面切换为新的主题风格。
因为不方便分析已经实现的源码,所以接下来的两篇文章都只是分析Android源码以及思路。先来看资源重定向的思路。

3 资源重定向:
先看一张重定向流程图:

其实思路就是这样的应用通过Resources类来获取资源的时候拦截一下,返回主题的资源。要想知道如何拦截,必须了解一下Resources类, 通过它取资源的流程,然后分析重定向的思路。
按照以下4个方面来分析:
3.1 Android apk资源的一些理解。
3.2 访问资源Resources类的构建。
3.3 通过Resources获取资源的流程(以获取图片为例)。
3.4 关于资源重定向思路分析

3.1 Android apk资源的一些理解:
R文件: 应用在编译完成后会生成一个R文件,在应用中定义的资源基本在R文件中对应一个Id,使用资源的时候都是通过这个ID来查找的。每个ID由三部分组成: packageID+TypeID+EntryID
resources.arsc文件: 这个文件是一个资源配置表,通过R文件中生成的资源ID可以从这个资源配置表中找到这个ID对应配置信息。这些配置信息中可以知道ID的资源类型,对应的名称/资源路径等等。
这里大概了解这两点就行了,足以应对主题功能了,其他更多的大家可以取看罗升阳的博客关于资源的介绍。

3.2 访问资源Resources类的构建:
应用都是通过Resources类来访问资源的,而主题就是希望能偷梁换柱替换资源,所以有必要了解一下Resources这个类。先看一张图整体了接Resources类相关:

ResourcesManager: 一个进程可以运行多个应用,也就是说可能存在多个Resources对象,所以ResourcesManager负责管理当前进程的所有Resources对象。
mActiveResources: ResourcesManager的成员变量,保存Resources对象。
Resources: 其实它只是对外提供一个访问资源的封装类,封装了AssertManager对象。
AssertManager(java层): 他只是封装了native层的AssertManager对象。
AssertManager(native层): 实际访问资源的类,下面是他的三个成员变量。
AssetPath: 表示资源的路径,一般包含两个, 一个是Framework-res.apk系统资源的路径(默认添加)另一个是,App所在的路径。
ResTable: 资源表,第一次获取资源的时候,会根据AssetPath给定的路径,读取路径中resources.arsc文件。
ResTable_Config: 当前设置的资源配置信息,在获取资源的过程中,会检查当前的配置信息,获取正确的资源信息。这个配置信息的值就是,语言,屏幕分辨率,橫竖屏等等。因为Android是支持多语言的,多分辨率的,想要找到合适的资源,就需要先设置这个Config。

总结一下:实际Resources就是构建了这三个成员变量,
其中AssetPath指明了资源从哪里找?
ResTable:找到后放在哪里?
ResTable_Config:如何找到合适当前设备的资源?
这里考虑文章的篇幅,不做源码分析,如果取分析ResourcesManager的getTopLevelResources函数你会发现实际上构造一个Resources类,就是为了构造Native层AssertManager的那三个成员变量。

3.3 通过Resources获取资源的流程(以获取图片为例)。
Resources是用来获取资源的,通过他获取图片资源的接口如下:
public Drawable getDrawable(@DrawableRes int id)
这个函数只是调用了另外一个getDrawable的函数

3.3.1 获取Drawable

    public Drawable getDrawable(int id, Theme theme){TypedValue value;getValue(id, value, true);final Drawable res = loadDrawable(value, id, theme);return res;}

这个函数主要做了两件事情,第一件事情:调用getValue函数获取一个 TypeValue。 第二件事情: 用TypeValue作为参数,调用loadDrawable函数获取Drawable对象。

3.3.2 getValue函数

    public void getValue(int id, TypedValue outValue, boolean resolveRefs){boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);}mAssets是前面讲过的java层的 AssertManager类的实例。具体函数如下final boolean getResourceValue(int ident,int density,TypedValue outValue,boolean resolveRefs){int block = loadResourceValue(ident,  (short) density,  outValue,  resolveRefs);....省略的代码和获取图片资源流程无关return false;}

这里loadResourceValue实际上是一个jni的函数,这个函数在android_util_assetmanager.cpp文件中。

3.3.3 loadResourceValue函数:

static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,jint ident,   jshort density,   jobject outValue, jboolean resolve){AssetManager* am = assetManagerForJavaObject(env, clazz);const ResTable& res(am->getResources());Res_value value;ResTable_config config;uint32_t typeSpecFlags;ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);uint32_t ref = ident;if (resolve) {block = res.resolveReference(&value, block, &ref,typeSpecFlags, &config);}if (block >= 0) {return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);}return static_cast<jint>(block);
}

前面讲过java层的AssetManager是封装了Native层的AssetManager对象。实际上就是Java层的AssetManager对象的成员变量mObject对应的就是Native层的一个地址,这个地址就是Native层的AssetManger对象。
a: 这里首先调用assetManagerForJavaObject把mObject转换成Native层的AssetManager
b: 调用AssetManger的getResources函数获取ResTable,前面分析过ResTab读取的是resources.arsc的信息,而resources.ars保存了资源的配置信息。
c: 调用ResTable的getResources函数获取C++层的Res_value,这个就是资源的配置信息,在C++层的表示。
d: 调用ResTable的resolveReference是处理引用类型的资源。在Android中定义可以资源可以引用另外一个资源,
f: 最后调用copyValue函数把Res_value 复制到TypeValue中。
分析到这里其实已经关联上了ResTable和TypedValue,具体的如何获取ResTab,ResTab如何通过iden和config匹配到合适的资源,如何处理引用类型的资源,这些过程会很长,而且分析流程就是为了找到Hook点,其实没有必要拦截到下面去,我曾分析过,试图从上层设置一个参数,在底层实现主题资源切换,但是没找到合适的地方。大家感兴趣可以继续分析。
接下来就看这个TypedValue是怎么用的。

3.3.4 loadDrawable()获取Drawable对象。
这个函数是重点分5个代码段来分析:

    Drawable loadDrawable(TypedValue value, int id, Theme theme) {//代码段1{}final boolean isColorDrawable;final DrawableCache caches;final long key;if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {isColorDrawable = true;caches = mColorDrawableCache;key = value.data;} else {isColorDrawable = false;caches = mDrawableCache;key = (((long) value.assetCookie) << 32) | value.data;}

代码段1{}
mColorDrawableCache: 用于缓存颜色资源的。
mDrawableCache: 用于缓存图片资源
通过前面获得的TypedValue的成员变量,assetCookie和data生成一个key。
这里再分享一个看代码的经验:如果不知道assetCookie和data表达什么意思,不打紧。主要是他们可以组合成一个key,通过分析下面的代码可以知道这个key就是用于缓存的。如果一定要和自己死磕看明白这两个变量是什么意思,你会发现,又会牵扯出大量的信息,信息量越大,越不利于分析代码逻辑,通常会大大降低看代码的效率。
这里还是简单说一下,不想了解的可以忽略:
assetCookie : 一般AssetManager有两个资源表,一个是Framework-res.apk,另外一个就是应用本身,放在一个数组里面。 当assetCookie=0 表示的是Framework-res.apk的资源,如果assetCookie = 1, 表示的是应用本省的资源。
data: 表示的是一个位置或者资源的值。当前这个资源在字符串资源池哪个block的位置。
type: 表示当前资源的类型。

当type为COLOR时: 为什么只用data为key呢?
原因是,可能在在应用定义多个Color但是他们表达的颜色是一样的,这样应用只要缓存一份就可以了,不用缓存多份。

        //代码段2{}if (!mPreloading) {final Drawable cachedDrawable = caches.getInstance(key, theme);if (cachedDrawable != null) {return cachedDrawable;}}

代码段2:
检查当前资源是否已经缓存过,如果缓存过直接放回。

        //代码段3{}final ConstantState cs;if (isColorDrawable) {cs = sPreloadedColorDrawables.get(key);} else {cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);}

代码段3:
sPreloadedColorDrawables:预加载的Color资源
sPreloadedDrawables:预加载的图片资源
Android系统会预加载一些常用的公共资源,类等。以提高运行效率。这些预加载的资源是在Zygote进程中加载的。而Android应用的进程都是由zygote进程fork出来的。因为Linux的fork特性,这些进程fork出来以后,预加载的资源就已经到内存里面了。
这段代码就是检查当前图片资源是否预加载的缓存中。

        //代码段4{}Drawable dr;if (cs != null) {dr = cs.newDrawable(this);} else if (isColorDrawable) {dr = new ColorDrawable(value.data);} else {dr = loadDrawableForCookie(value, id, null);}

这段代码是如果缓存前面的缓存里面存在,就直接取出drawable,如果没有就调用loadDrawableForCookie函数加载drawable。这个函数稍后分析。

        //代码段5{}if (dr != null) {dr.setChangingConfigurations(value.changingConfigurations);cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);}

这段代码主要是把代码段4获取的Drawable缓存起来。保证第二次获取的时候就直接从缓存中读。
return dr;
}

代码段4{} loadDrawableForCookie函数:

 private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {final String file = value.string.toString();final Drawable dr;if (file.endsWith(".xml")) {final XmlResourceParser rp = loadXmlResourceParser(file, id, value.assetCookie, "drawable");dr = Drawable.createFromXml(this, rp, theme);rp.close();} else {final InputStream is = mAssets.openNonAsset(value.assetCookie, file, AssetManager.ACCESS_STREAMING);dr = Drawable.createFromResourceStream(this, value, is, file, null);is.close();}return dr;}

TypedValue的成员变量string: 原来当资源是图片是,string表示的文件名。
当文件名是.xml文件时,调用另外一个函数取解析获取drawable,实际上你会发现,这种情况一班是,shape,selector之类的资源,里面还是Drawable最终还是会调用到下面else的流程来获取Drawable。
当文件名不是以.xml文件时,通过AssetManager获取drawable文件的InputStream,然后创建Drwable对象。
总结一下: 在图片资源获取流程中,首先是获取该资源的TypedValue,然后通过TypedValue的成员变量assetCookie和data 生成图片资源的key, 再通过key检查当前的图片资源是否已经在缓存中,如果没有缓存,就通过TypedValue的成员变量string找到当前图片资源的文件名。通过AssertManager对象找到文件的InputStream创建Drawable对象。
这个TypedValue是一个很重要的变量。大家分析其他资源,比如string,color,dimen,的获取流程,会发现这些资源的值,就保存在他的成员变量data中。

3.4 关于资源重定向思路分析
前面的分析已经大概了解Resources类,以及通过他获取资源的流程。而文档中也给出了,重定向资源,就是在应用通过Resources类来获取资源的时候拦截一下,返回主题的资源。
对于图片资源而言,其实就是在上面的流程中找一个地方,把代码段4{} loadDrawableForCookie函数分析中,通过AssertManager拿到InputStream创建的Drawable替换掉,具体替换的点,大家可以自己选,但是有一个问题应该考虑,那就是你也要遵从Android 对资源的缓存机制。 或者说自己加一套缓存,遵从图片获取的流程。
对于其他资源而言,也分析过,TypedValue的data成员变量,保存了他们值。

4 主题资源的存放格式:
在《MIUI主题风格_一种Android系统换肤功能的设计》 的文档里面已经说的很清除了,他也提到,主题资源需要手动解析的?这个手动解析是什么意思呢?
正常我们定义的string,color资源,在aapt编译的时候,就把这些资源对应的值已经解析出来了。我们可以通过Resources类的接口把值读出来。
因为主题的资源是没有被系统编译过的,所以需要手动解析,解析出来的值,应该和aapt编译string,color,dimen解析出来的值一样。
如果大家在手动解析过程中遇到什么问题,可以参考aapt是如何编译string,color,dimen资源的。
欢迎关注个人微信公众号,定期更新个人工作心得

小米多主题思路分析-重定向资源篇相关推荐

  1. hadoop作业初始化过程详解(源码分析第三篇)

    (一)概述 我们在上一篇blog已经详细的分析了一个作业从用户输入提交命令到到达JobTracker之前的各个过程.在作业到达JobTracker之后初始化之前,JobTracker会通过submit ...

  2. PEInfo编程思路讲解01 - 工具篇01|解密系列

    PEInfo编程思路讲解01 - 工具篇01 让编程改变世界 Change the world by program   软件安全是信息安全领域的重要内容,本系列视频教程将涉及到软件相关的加密.解密. ...

  3. PEInfo编程思路讲解03 - 工具篇03|解密系列

    PEInfo编程思路讲解03 - 工具篇03 让编程改变世界 Change the world by program   软件安全是信息安全领域的重要内容,本系列视频教程将涉及到软件相关的加密.解密. ...

  4. android点击事件的优先级,Android事件体系全面总结+实践分析,系列篇

    前言 在这一个月里,我利用闲余的时间看了下最近Android职业发展这块该怎么选择?这个问题各位大神的回答都非常透彻,相信对大家或多或少都在一定程度上有很大的帮助,今天在这里写这篇文章更多的是想以我开 ...

  5. 工作分析文献综述_北大教授分析了124 篇不合格硕士学位论文,发现了6个典型问题!...

    原标题:北大教授分析了124 篇不合格硕士学位论文,发现了6个典型问题! 根据词条的词频统计状况,按占比情况由高到低排列,不合格学位论文大致存在"作者科研能力不足""论文 ...

  6. 任正非:华为鸿蒙将比安卓快 60%;小米回应主题侵权;VS Code 1.36发布​ | 极客头条...

    快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「CSDN 极客头条」,是从 CSDN 网站延伸至官方微信公众号的特别栏目,专注于一天业界事报道.风里雨里,我们将每天为朋友们,播报最新鲜有 ...

  7. 2022华数杯B题论文思路分析+完整代码(水下机器人组装计划)(一二问答案接出来和标准答案一样)(问题三四逼近正确答案)(完整论文,代码可直接跑)

    写在前面:学校最近搞数学建模竞赛培训,以2022华数杯B题作为训练题目,在查资料过程中发现网上没有哪一篇论文解出了正确答案,而我们组利用Lingo软件准确的解出了正确答案,但是在第三问时,由于决策的变 ...

  8. 初中数学抽象教学的案例_初中数学教学案例分析论文2篇

    日记网 >> 专题 初中数学教学案例分析论文2篇 更新时间:2018/6/18 0:45:00  点击率:1489  手机版 初中数学教学案例分析论文2篇来自简单日记网精选推荐.教育必须把 ...

  9. Wireshark数据抓包分析——网络协议篇

    Wireshark数据抓包分析--网络协议篇 Wireshark是目前最受欢迎的抓包工具.它可以运行在Windows.Linux及MAC OS X操作系统中,并提供了友好的图形界面.同时,Wiresh ...

  10. python商品评论分析_NLP实战:用主题建模分析网购评论(附Python代码)

    现在电商行业势头正好,对在线零售商来说,他们不受库存或空间的限制,而实体店则必须在有限的空间中存储产品. 但是,在线购物也有它的局限之处,最大的难题之一就是检验产品的真伪.它的质量是否如宣传所说的那么 ...

最新文章

  1. 前端开发之retina屏幕
  2. VS2017调用MySQL 8.0(附上C++程序)
  3. php短信接口源码,比较简单,但也实用
  4. 易格斯拖链选型手册_拖链相关知识
  5. Vue安装npm长时间停留WARN deprecated request@2.88.2: request has been deprecated
  6. 看以色列话剧《安魂曲》(图)
  7. Istio 在阿里云容器服务的部署及流量治理实践
  8. 草地排水-网络流dinic
  9. movingpandas时空数据分析——旧金山出租车轨迹数据集处理
  10. 施乐3030服务器系统安装,施乐DW3030驱动安装步骤
  11. ssh autologin REMOTE HOST IDENTIFICATION HAS CHANGED处理
  12. Android摄影App,摄影爱好者必备的Android摄影App推介
  13. ​Copyright到底是什么意思?
  14. 小程序css样式变量/api promise化
  15. 剑灵系统推荐加点_《剑灵》各职业练级推荐修炼加点攻略
  16. qq客服不需要加好友的html方法
  17. 如何在PPT中实现多张图片叠加在一起,点击消失一张出来下一张的效果
  18. 希捷和西数移动硬盘哪个好_西数和希捷的硬盘哪个好看完存储数据一目了然
  19. 多种UI和界面设计汇总(一)
  20. 详解机器学习基础--线性回归算法

热门文章

  1. 加密软件VMProtect入门教程
  2. 【NLP】统计自然语言处理(第2版)思维导图
  3. C++ 学到什么程度可以找工作?
  4. 华为交换机关闭网口_华为交换机如何关闭网络端口号
  5. pop,oop,aop编程思想
  6. 智能优化算法——蝙蝠算法(PythonMatlab实现)
  7. Lenovo System x 硬件Windows Server驱动下载
  8. java根据中文汉字获取拼音——java
  9. 震旦adc225打印机连接计算机,震旦ADC225打印机驱动
  10. StrongShop跨境电商系统源码 | 支持多语言多货币