前言
什么是资源冲突
引用第三方库或者子Module时,这些依赖库中的资源可能会与主工程本身的资源发生重名冲突,从而发生资源互相覆盖的情况
容易导致什么问题
- 项目引用drawable资源、string资源等可能不会按照自己预期的效果来显示
- layout资源如果错误,甚至会导致无法通过id获取到对应控件,从而触发如空指针等一系列崩溃问题
以上的问题都是可能需要到运行时才会发现的
处理办法
修改资源前缀
可以通过在model的gradle文件里加上下面的字段
android{
resourcePrefix 'a_'
}
注意:上面只是加上了限制,让你的module不按照前缀规范(如a_activity_main.xml)来命名的资源会在编译器中报错,并且此报错并不会影响编译…所以这个方法仅仅适用于小项目或者项目的初期
通过CheckResourceConflict工具
CheckResourceConflict的github地址
工具作用
新增了一个task,可以用来遍历所有资源文件,根据他们所在的目录与文件名/资源名组成id,之后比较它们的id即可
如何生成task并获取所有资源文件
class CheckResourcePrefixPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.afterEvaluate {
variants.forEach { variant ->
variant as BaseVariantImpl
def thisTaskName = "checkResource${variant.name.capitalize()}"
//每个variant都创建一个任务
def thisTask = project.task(thisTaskName)
thisTask.group = "check"
thisTask.doLast {
//这里获取到了所有资源文件
def files = variant.allRawAndroidResources.files
}
}
}
}
}
如何区分资源
根据所在父文件夹、文件名/资源名来判断是否会冲突
//FileResource.java
public String getUniqueId() {
//在drawable或者mipmap文件夹中
//例:file@drawable-v4/abc
return this.lastDirectory != null && (this.lastDirectory.startsWith("drawable-")
|| this.lastDirectory.startsWith("mipmap-"))
&& !this.lastDirectory.contains("-v") ? "file@" + this.lastDirectory + "-v4/" + this.fileName : "file@" + this.lastDirectory + "/" + this.fileName;
}
//ValueResource.java
public String getUniqueId() {
//例:value@values/app_name
return "value@" + this.lastDirectory + "/" + this.resName;
}
AndResGuard进行资源混淆
作用
抄自官方解释:AndResGuard是一个帮助你缩小APK大小的工具,他的原理类似Java Proguard,但是只针对资源。他会将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a。
自己的理解
- 它会把原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a,从而减少apk大小,并且让别人
- 它从字节码层面修改了resources.arsc文件(即资源的引用路径、值的索引表),它把里面的name换成了相同值,并指向了我们所修改了路径的资源文件,从而也节省了部分空间
- 注意修改name会导致Resources的getIdentifier无效,就是无法通过name反查id,这会与android-skin-support换肤框架冲突,所以如果有这些api或者框架的使用,需要在框架中通过相应配置关闭name的重写
最终apk打包的效果