改不完的 Bug,写不完的矫情。公众号 杨正友 现在专注移动基础平台开发 ,涵盖音视频, APM和信息安全等各个知识领域;只做全网最 Geek 的公众号,欢迎您的关注!精彩内容不容错过~
大家都知道 png 是比较占用App体积的,有没有工具可以在打包前(比如 assembleDebug、assembleRelease)自动去转化所有的 png 图片,包括第三方依赖库里面的呢?之前不经意间发现有一个神器cwebp 转化工具,是不是可以借鉴这种工具自己写个Plugin完成图片转换,同时支持检查大图片,图片大小可配置。话不多说,说干就干~
编写插件前,需要思考几个业务痛点
鉴于问题1,我们可以参考McImage, 其实也很简单,就是一个 Gradle API,看链接文档的文档即可
鉴于问题2,该 Task 的执行时机其实是依赖于 MergeResources Task
鉴于问题3, 我们可以通过 Gradle API 自定义API 设置开关,图片最大体积,给图片添加白名单
convert2WebpConfig{
enableWhenDebug true
maxSize 1024*1024 // 1M
whiteList ["xxx.png","xxx.png"]
//...
}
实现自定义属性图片转换器开关配置,图片最大体积配置,图片添加白名单配置
kapt "com.google.auto.service:auto-service:1.0-rc4"
implementation "com.google.auto.service:auto-service:1.0-rc4"
compileOnly "com.android.tools.build:gradle:4.0.1"
testCompileOnly "com.android.tools.build:gradle:4.0.1"
利用AutoService注解,使用的反射去实例化对象 VariantProcessor
,动态注册Convert2WebpTask任务,后期使用该注解处理器处理Convert2WebpTask任务
@AutoService(VariantProcessor::class)
class Convert2WebpVariantProcessor : VariantProcessor {
override fun process(variant: BaseVariant) {
val variantData = (variant as ApplicationVariantImpl).variantData
val tasks = variantData.scope.globalScope.project.tasks
val convert2WebpTask = tasks.findByName("convert2Webp") ?: tasks.create(
"convert2Webp",
Convert2WebpTask::class.java
)
val mergeResourcesTask = variant.mergeResourcesProvider.get()
mergeResourcesTask.dependsOn(convert2WebpTask)
}
}
if (!config.enableWhenDebug) {
return@all
}
val dir = variant.allRawAndroidResources.files
/**
* 遍历资源文件目录
*/
for (channelDir in dir) {
traverseResDir(channelDir, imageFileList, cacheList, object : IBigImage {
override fun onBigImage(file: File) {
bigImageList.add(file.absolutePath)
}
})
}
private fun traverseResDir(
file: File,
imageFileList: ArrayList<File>,
cacheList: ArrayList<String>,
iBigImage: IBigImage
) {
if (cacheList.contains(file.absolutePath)) {
return
} else {
cacheList.add(file.absolutePath)
}
if (file.isDirectory) {
file.listFiles()?.forEach {
if (it.isDirectory) {
traverseResDir(it, imageFileList, cacheList, iBigImage)
} else {
filterImage(it, imageFileList, iBigImage)
}
}
} else {
filterImage(file, imageFileList, iBigImage)
}
}
/**
* 过滤图片
*/
private fun filterImage(file: File, imageFileList: ArrayList<File>, iBigImage: IBigImage) {
// 如果添加了图片白名单或者文件不是图片格式,过滤
if (config.whiteList.contains(file.name) || !ImageUtil.isImage(file)) {
return
}
// 如果图片尺寸合规,并且图片是大图,大图白名单没有图片
if ((config.isCheckSize && ImageUtil.isBigSizeImage(file, config.maxSize))
&& !config.bigImageWhiteList.contains(file.name)
) {
// 添加到大图列表
iBigImage.onBigImage(file)
}
// 将图片添加到图片图片目录
imageFileList.add(file)
}
private fun checkBigImage() {
if (bigImageList.size != 0) {
val stringBuffer = StringBuffer("Big Image Detector! ")
.append("ImageSize can't over ${config.maxSize / 1024}kb.\n")
.append("To fix this exception, you can increase maxSize or config them in bigImageWhiteList\n")
.append("Big Image List: \n")
for (fileName in bigImageList) {
stringBuffer.append(fileName)
stringBuffer.append("\n")
}
throw GradleException(stringBuffer.toString())
}
}
private fun dispatchOptimizeTask(imageFileList: java.util.ArrayList<File>) {
if (imageFileList.size == 0 || bigImageList.isNotEmpty()) {
return
}
val coreNum = Runtime.getRuntime().availableProcessors()
if (imageFileList.size < coreNum) {
for (file in imageFileList) {
optimizeImage(file)
}
} else {
val results = ArrayList<Future<Unit>>()
val pool = Executors.newFixedThreadPool(coreNum)
val part = imageFileList.size / coreNum
for (i in 0 until coreNum) {
val from = i * part
val to = if (i == coreNum - 1) imageFileList.size - 1 else (i + 1) * part - 1
results.add(pool.submit(Callable<Unit> {
for (index in from..to) {
optimizeImage(imageFileList[index])
}
}))
}
for (f in results) {
try {
f.get()
} catch (e: Exception) {
println("EHiPlugin Convert2WebpTask#dispatchOptimizeTask() execute wrong.")
}
}
}
}
/**
* 压缩图片
*/
private fun optimizeImage(file: File) {
val path: String = file.path
if (File(path).exists()) {
oldSize += File(path).length()
}
ImageUtil.convert2Webp(file)
calcNewSize(path)
}
private fun optimizeImage(file: File) {
val path: String = file.path
if (File(path).exists()) {
oldSize += File(path).length()
}
ImageUtil.convert2Webp(file)
calcNewSize(path)
}
dispatchOptimizeTask(imageFileList)
println("Before optimize Size: ${oldSize / 1024}kb")
println("After optimize Size: ${newSize / 1024}kb")
println("Optimize Size: ${(oldSize - newSize) / 1024}kb")
println("CostTotalTime: ${System.currentTimeMillis() - startTime}ms")
println("------------------------------------")
本文主要是在打包前对App做了一次图片全量替换,图片转换方式借助的是Google开源工具cwebp,当然我们可以通过白名单方式规范图片尺寸大小和插件开关,如果你掌握本文内容,不仅会对你们公司应用瘦身有所帮助,同时也能弥补你对 Gradle Plugin 知识的渴望~
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。