组件化项目资源都是以组件名开头比如home_bg_main.png。因此经常会出现同样的图片在多个组件存放,名字不同。是包大小快速膨胀。因此对于组件化项目我们得想办法去除重复的图片,避免安装包大小膨胀。
项目build.gradle中添加如下引用
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"//gradle版本号大于4.0.1
classpath 'io.github.cnoke:resplugin:?'//版本用上面maven centeral显示版本
}
}
app build.gradle中添加如下引用
plugins {
id 'com.android.application'
id 'resplugin'//添加该插件
}
通过如上步骤就能开启资源文件去重,避免组化项目资源文件重复
如果需要控制插件的开启关闭,忽略某些文件可在app build.gradle增加如下配置
//插件配置,默认情况无需配置
ResDeduplication{
enable = true //是否打开插件
enableWhenDebug = true //debug模式是否使用插件
ignoreFileName = ["111.png","222.jpg"]//忽略文件列表
}
app打包是gradle通过一个一个task完成。processDebugResources是task使用aapt 打包资源。那么在processDebugResources执行完成之后,我们可以插入自己的task做如下操作:
- 解压文件
- 通过对比文件 CRC-32判断文件是否相同。
- 如果相同,修改resources.arsc把重复的资源都重定向到一个资源上
- 保留引用资源,删除其他重复资源
- 重新压缩文件
/**
* 删除重复资源
*/
private fun deleteRepeatResources(unZipDir: String,mappingFile: File, apFile: File, ignoreFileName: Array<String>?){
val fileWriter = FileWriter(mappingFile)
// 查询重复资源
val groupResources = ZipFile(apFile).groupsResources()
// 获取
val resourcesFile = File(unZipDir, RESOURCES_NAME)
val newResouce = FileInputStream(resourcesFile).use { fileInput ->
val resouce = ResourceFile.fromInputStream(fileInput)
groupResources
.asSequence()
.filter {
it.value.size > 1
}
.filter { filter ->
// 过滤
val name = File(filter.value[0].name).name
ignoreFileName?.contains(name)?.let { !it } ?: true
}
.forEach { zipMap->
val zips = zipMap.value
val coreResources = zips[0]
for (index in 1 until zips.size) {
// 重复的资源
val repeatZipFile = zips[index]
fileWriter.synchronizedWriteString("${repeatZipFile.name} => ${coreResources.name}")
// 删除解压的路径的重复文件
File(unZipDir, repeatZipFile.name).delete()
// 将这些重复的资源都重定向到同一个文件上
resouce
.chunks
.asSequence()
.filter {
it is ResourceTableChunk
}
.map {
it as ResourceTableChunk
}
.forEach { chunk ->
val stringPoolChunk = chunk.stringPool
val index = stringPoolChunk.indexOf(repeatZipFile.name)
if (index != -1) {
stringPoolChunk.setString(index, coreResources.name)
}
}
}
}
fileWriter.close()
resouce
}
resourcesFile.delete()
FileOutputStream(resourcesFile).use {
it.write(newResouce.toByteArray())
}
}