手把手讲解 Android hook技术实现一键换肤

本文手把手教你如何使用Hook技术实现Android应用的一键换肤功能,避免逐个View处理的繁琐操作。通过hook系统创建View的过程,替换资源文件,实现全App范围的换肤,同时探讨相关Android源码,包括Activity的setContentView、LayoutInflater和资源管理等。
摘要由CSDN通过智能技术生成

前言

手把手讲解系列文章,是我写给各位看官,也是写给我自己的。
文章可能过分详细,但是这是为了帮助到尽量多的人,毕竟工作5,6年,不能老吸血,也到了回馈开源的时候.
这个系列的文章:
1、用通俗易懂的讲解方式,讲解一门技术的实用价值
2、详细书写源码的追踪,源码截图,绘制类的结构图,尽量详细地解释原理的探索过程
3、提供Github 的 可运行的Demo工程,但是我所提供代码,更多是提供思路,抛砖引玉,请酌情cv
4、集合整理原理探索过程中的一些坑,或者demo的运行过程中的注意事项
5、用gif图,最直观地展示demo运行效果

如果觉得细节太细,直接跳过看结论即可。
本人能力有限,如若发现描述不当之处,欢迎留言批评指正。

学到老活到老,路漫漫其修远兮。与众君共勉 !


引子

产品大佬又提需求啦,要求app里面的图表要实现白天黑夜模式的切换,以满足不同光线下都能保证足够的图表清晰度. 怎么办?可能解决的办法很多,你可以给图表view增加一个toggle方法,参数Stringday/night,然后切换之后postInvalidate 刷新重绘.
OK,可行,但是这种方式切换白天黑夜,只是单个View中有效,那么如果哪天产品又要另一个View换肤,难道我要一个一个去写toggle么?未免太low了.

那么能不能要实现一个全app内的一键换肤,一劳永逸~~~


鸣谢

感谢享学课堂的免费视频课程 https://ke.qq.com/course/341933 需要视频的兄弟可以给我留言评论


正文大纲

1. 什么是一键换肤

2. 界面上哪些东西是可以换肤的

3. 利用HOOK技术实现优雅的“一键换肤"

4. 相关android源码一览

  • Activity 的 setContentView(R.layout.XXX) 到底在做什么?
  • LayoutInflater这个类是怎么把 layout.xml 的 <TextView> 变成TextView对象的?
  • app中资源文件大管家 Resources / AssetManager 是怎么工作的

5. "全app一键换肤" Demo源码详解

  • 关键类 SkinEngine SkinFactory
  • 关键类的调用方式,联系之前的android源码,解释hook起作用的原理
  • 效果展示
  • 注意事项

正文

1. 什么是一键换肤

所谓"一键",就是通过"一个"接口的调用,就能实现全app范围内的所有资源文件的替换.包括 文本,颜色,图片等.

一些换肤实现方式的对比

  • 方案1:自定义View中,要换肤,那如同引言中所述,toggle方法,invalidate重绘。
    弊端:换肤范围仅限于这个View.
  • 方案2:给静态变量赋值,然后重启Activity. 如果一个Activity内用静态变量定义了两种色系,那么确实是可以通过关闭Activity,再启动的方式,实现 貌似换肤的效果(其实是重新启动了Activity)
    弊端:太low,而且很浪费资源

也许还有其他方案吧,View重绘,重启Activity,都能实现,但是仍然不是最优雅的方案,那么,有没有一种方案,能够实现全app内的换肤效果,又不会像重启 Activity 这样浪费资源呢?请看下图:

 

这个动态图中,首先看到的是Activity1,点击换肤,可直接更换界面上的background,图片的src,还有textViewtextColor,跳转Activity2之后的textView颜色,在我换肤之前,和换肤之后,是不同的。换肤的过程我并没有启动另外的Activity,界面也没有闪烁。我在Activity1里面换肤,直接影响了Activity2textView字体颜色。

既然给出了效果,那么肯定要给出Demo,不然太没诚意,嘿嘿嘿
github地址奉上:https://github.com/18598925736/HookSkinDemoFromHank


2. 界面上哪些东西是可以换肤的

上面的换肤动态图,我换了ImageView,换了background,换了TextView的字体颜色,那么到底哪些东西可以换?

答案其实就一句话: 我们项目代码里面 res目录下的所有东西,几乎都可以被替换。
(为什么说几乎?因为一些犄角旮旯的东西我没有时间一个一个去试验....囧)

具体而言就是如下这些

  • 动画
  • 背景图片
  • 字体
  • 字体颜色
  • 字体大小
  • 音频
  • 视频

3. 利用HOOK技术实现优雅的“一键换肤"

  • 什么是hook
    如题,我是用hook实现一键换肤。那么什么是hook?
    hook,钩子. 安卓中的hook技术,其实是一个抽象概念:对系统源码的代码逻辑进行"劫持",插入自己的逻辑,然后放行。注意:hook可能频繁使用java反射机制···

"一键换肤"中的hook思路

  1. "劫持"系统创建View的过程,我们自己来创建View
    系统原本自己存在创建View的逻辑,我们要了解这部分代码,以便为我所用.
  2. 收集我们需要换肤的View(用自定义view属性来标记一个view是否支持一键换肤),保存到变量中
    劫持了 系统创建view的逻辑之后,我们要把支持换肤的这些view保存起来
  3. 加载外部资源包,调用接口进行换肤
    外部资源包,是.apk后缀的一个文件,是通过gradle打包形成的。里面包含需要换肤的资源文件,但是必须保证,要换的资源文件,和原工程里面的文件名完全相同.

4. 相关android源码一览

  • Activity 的 setContentView(R.layout.XXX) 到底在做什么?
    回顾我们写app的习惯,创建Activity,写xxx.xml,在Activity里面setContentView(R.layout.xxx). 我们写的是xml,最终呈现出来的是一个一个的界面上的UI控件,那么setContentView到底做了什么事,使得XML里面的内容,变成了UI控件呢?

如果不先来点干货,估计有些人就看不下去了,各位客官请看下图:

 

 

源码索引:
setContentView(R.layout.activity_main);
---》
getDelegate().setContentView(layoutResID);

OK,这里暴露出了两个方法,getDelegate()setContentView()

先看getDelegate:
这里返回了一个AppCompatDelegate对象,跟踪到AppCompatDelegate内部,阅读源码,可以得出一个结论:AppCompatDelegate 是 替Activity生成View对象的委托类,它提供了一系列setContentView方法,在Activity中加入UI控件。
那它的AppCompatDelegatesetContentView方法又做了什么?

插曲:关于如何阅读源码?在我的上一篇文章 中详细说明了。
但是漏了一个细节:那就是,当你在源码中看到一个接口或者抽象类,你想知道接口的实现类在哪?很简单...如果你没有更改androidStudio的快捷键设置的话,Ctrl+T可以帮你直接定位 接口和抽象类的实现类.

用上面的方法,找到setContentView的具体过程

 

 

那么就进入下一个环节:LayoutInflater又做了什么?

  • LayoutInflater这个类是怎么把layout.xml<TextView> 变成TextView对象的?
    我们知道,我们传入的是int,是xxx.xml这个布局文件,在R文件里面的对应int值。LayoutInflater拿到了这个int之后,又干了什么事呢?

一路索引进去:会发现这个方法:

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值