1. 问题抛出
1.1 开发方面
对于日常开发中,每写一个"#333",都要手动的在当前xml与colors.xml中来回切换,查看是否已经定义过,如果定义过则拿过来复用,如果没有就要新定义一个叫"#333"的资源名,然后再使用。同样的情况存在于dimen以及string,这无疑是重复冗余的操作。
1.2 维护方面
试想一下,你接手了一个老项目,发现color、string和dimen信息全部以硬编码的形式存在于项目的xml文件中,而项目中的xml文件成百上千,那么提取工程中全部硬编码的工作就会变得异常繁琐。
2. 插件解决
因此综上所述,考虑做一个AS的插件,可以解决上述两个问题。先看一下插件使用的效果:
2.1 插件安装
首先在AS的插件商店里进行搜索YanXuan HardCode Opt进行下载安装。
2.2 对于单个文件的快捷键一键操作
快捷键为Commend+Ctrl+S,该操作会包含对单个文件的color、string和dimen三种硬编码提取,需要注意的是,需要在你写完代码之后,先Commend+S保存内容,再执行插件,否则会有文件缓存冲突的弹窗:
2.3 对于文件夹下的批量操作
右键选择你要执行插件的文件夹,可以选择其中一种硬编码提取,将对所选文件夹下的所有xml文件全部执行优化,并在log中提示执行结果,这里以color优化为例:
3. 插件解决的三个问题
3.1 哪几个层级可以执行插件
- 在res/layoutXXX/或者res/drawableXXX/下面的xml文件
- res目录下的/layoutXXX/或者res/drawableXXX命名的文件夹
3.2 怎么去查找硬编码
- IntelliJ 的虚拟文件系统VirtualFile
- xml解析将文件流转成Node树,再查找属性值
- string查找:文案千变万化,毫无规律可言,所以没法从string的规律去查找,只能从指定的属性去查找, 即android:text和android:hint属性
- color和dimen的查找:不同于string,color以及dimen是可以通过正则表达式实现筛选的:
Pattern.compile(“^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$");
Pattern.compile("([1-9]\\d{0,3}|0)[.]{0,1}[5]{0,1}dp");
- 如果执行dimen优化,类似于xdp的值可以被识别,其中x是大于0的正整数或点5的小数,0<x<10000
- 如果执行color优化,类似于#xxx、#xxxxxx、#xxxxxxxx的值可以被识别
- 已过滤databinding的样式
3.3 找到之后如何替换
- 对于dimen和color会优先取缓存,如果命中,则直接取出已有资源文件中的name,不改动value文件中的内容。而string不会读缓存,总会生成新的值
- 直接修改Node数值的方式,这种方式会修改原有的代码(比如会格式化原有的代码)为了尽量不改动原有代码,放弃该方案
- 把整个文件看成一整个字符串,通过String.indexOf的方式找到待更换的位置,使用replace的方式替换子字符串,再读写文件完成替换
- values目录,以及dimen/string/color文件会自动识别或生成
- 对于新资源的命名:
- color以color_xxx来命名;
- string以layout's name_text_index来命名;
- dimen以size_xxdp来命名,带.5的小数以下划线代替小数点。
4. 插件使用时的注意事项
- 对于单个文件,支持右键以及快捷键两种方式,快捷键默认三种优化一起执行。需要在你写完代码之后,先Commend+S保存内容,再执行插件,否则会有文件缓存冲突的弹窗
- 对于文件夹,为了性能,仅支持右键选择一种优化方式
- 在插件执行完毕后,会在Event Log中输出执行结果,color以及dimen会复用你工程中已存在的item value,而string因为与业务强绑定,因此只会生成一个新的item value
- 欢迎大家下载尝试并提出意见~