使用
classpath 'com.smallsoho.mobcase:McImage:1.5.1'
apply plugin: 'McImage'
McImageConfig {
isCheckSize true
optimizeType "ConvertWebp" //Optimize Type,"ConvertWebp" or "Compress",default "Compress", "CompressWebp" is a better compression ratio but it don't support api < 18
maxSize 1*1024*1024 //big image size threshold,default 1MB
enableWhenDebug true //switch in debug build,default true
isCheckPixels true // Whether to detect image pixels of width and height,default true
maxWidth 1200 //default 1000
maxHeight 1000 //default 1000
whiteList = [
]
mctoolsDir "$rootDir"
isSupportAlphaWebp true
multiThread true
bigImageWhiteList = [
"icon_login_bg.png"
]
}
解释下下面参数的意思:
isCheckSize:是否检查图片的大小
optimizeType:方式,包括压缩和转化成Webp格式
maxSize:大图的判定条件,如果超过这个就会报错,要想不报错,则将改图片放到bigImageWhiteList中,这样就会跳过大图片检测。
enableWhenDebug:是否在dengbug进行build。
isCheckPixels:检查图片的宽和高,如果超过这个就会报错,其限制为maxWidth和maxHeight参数决定
whiteList:加入这个list的不会进行转变
multiThread:是否支持多线程
bigImageWhiteList:加入到这个的图片不需要进行大图检测
源码分析
override fun apply(project: Project) {
mcImageProject = project
//check is library or application
val hasAppPlugin = project.plugins.hasPlugin("com.android.application")
val variants = if (hasAppPlugin) {
(project.property("android") as AppExtension).applicationVariants
} else {
(project.property("android") as LibraryExtension).libraryVariants
}
//set config
project.extensions.create("McImageConfig", Config::class.java)
mcImageConfig = project.property("McImageConfig") as Config // 1
project.afterEvaluate {
variants.all { variant ->
variant as BaseVariantImpl
checkMcTools(project)//2
val mergeResourcesTask = variant.mergeResourcesProvider.get()//3
val mcPicTask = project.task("McImage${variant.name.capitalize()}")//4
mcPicTask.doLast {
val dir = variant.allRawAndroidResources.files
val cacheList = ArrayList<String>()
val imageFileList = ArrayList<File>()
for (channelDir: File in dir) {
traverseResDir(channelDir, imageFileList, cacheList, object : IBigImage {
override fun onBigImage(file: File) {
bigImgList.add(file.absolutePath)
}
})
}
checkBigImage()//5
val start = System.currentTimeMillis()
mtDispatchOptimizeTask(imageFileList)//6
LogUtil.log(sizeInfo())
LogUtil.log("---- McImage Plugin End ----, Total Time(ms) : ${System.currentTimeMillis() - start}")
}
//chmod task
val chmodTaskName = "chmod${variant.name.capitalize()}"
val chmodTask = project.task(chmodTaskName)//7
chmodTask.doLast {
//chmod if linux
if (Tools.isLinux()) {
Tools.chmod()
}
}
//inject task
(project.tasks.findByName(chmodTask.name) as Task).dependsOn(mergeResourcesTask.taskDependencies.getDependencies(mergeResourcesTask))
(project.tasks.findByName(mcPicTask.name) as Task).dependsOn(project.tasks.findByName(chmodTask.name) as Task)
mergeResourcesTask.dependsOn(project.tasks.findByName(mcPicTask.name))//8
}
}
}
1处可以理解就是创建一个java bean类对象,其赋值则是上面的McImageConfig里面的东西。并且在build.gradle文件中的McImageConfig这个名字是根据project.extensions.create("McImageConfig", Config::class.java)确定的,而且必须要一致,不然会报错。具体字段如下:
public class Config {
public static final String OPTIMIZE_WEBP_CONVERT = "ConvertWebp"; //webp化
public static final String OPTIMIZE_COMPRESS_PICTURE = "Compress"; //压缩图片
public float maxSize = 1024 * 1024;
public boolean isCheckSize = true; //是否检查大体积图片
public String optimizeType = OPTIMIZE_WEBP_CONVERT; //优化方式,webp化、压缩图片
public boolean enableWhenDebug = true;
public boolean isCheckPixels = true; //是否检查大像素图片
public int maxWidth = 1000;
public int maxHeight = 1000;
public String[] whiteList = new String[]{}; //优化图片白名单
public String mctoolsDir = "";
public boolean isSupportAlphaWebp = false; //是否支持webp化透明通道的图片,如果开启,请确保minSDK >= 18,或做了其他兼容措施
public boolean multiThread = true;
public String[] bigImageWhiteList = new String[]{}; //大图检测白名单
}
在2处检查工具,即转化成webP格式的在window,mac,linux平台下的工具。
下载下来需要放在根目录下。
下载路径 https://github.com/smallSohoSolo/McImage/releases
3处则是拿到mergeDebugResourcesTask,一般执行mergeDebugResources会将所有的资源进行合并,这样我们就可拿到所有的资源文件。
而我们的插件则是在这一步之后,遍历拿到所有的图片资源进行操作。
4处则是创建两个个task,分别有debug和release。创建之后可以在AS的右侧的gradle的other文件夹看到这两个task。
然后执行dolast里面的东西。拿到所有的资源文件,然后进行遍历。
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)
}
}
在这里采用递归的方式,并且对图片进行一个过滤。
在5处继续对大图进行检查。
private fun checkBigImage() {
if (bigImgList.size != 0) {
val stringBuffer = StringBuffer("You have big Imgages with big size or large pixels," +
"please confirm whether they are necessary or whether they can to be compressed. " +
"If so, you can config them into bigImageWhiteList to fix this Exception!!!\n")
for (i: Int in 0 until bigImgList.size) {
stringBuffer.append(bigImgList[i])
stringBuffer.append("\n")
}
throw GradleException(stringBuffer.toString())
}
}
如果在build.gradle里面配置了,则不满足条件的则报异常。
6处则是判定是否进行多线程进行图片处理。
7处则是有重新创建一个task。
在8处则是对创建的task的一个先后执行的顺序。
在项目中进行webp图片转换是在WebpUtils类中。
private fun formatWebp(imgFile: File) {
if (ImageUtil.isImage(imgFile)) {
val webpFile = File("${imgFile.path.substring(0, imgFile.path.lastIndexOf("."))}.webp")
Tools.cmd("cwebp", "${imgFile.path} -o ${webpFile.path} -m 6 -quiet")
if (webpFile.length() < imgFile.length()) {
LogUtil.log(TAG, imgFile.path, imgFile.length().toString(), webpFile.length().toString())
if (imgFile.exists()) {
imgFile.delete()
}
} else {
//如果webp的大的话就抛弃
if (webpFile.exists()) {
webpFile.delete()
}
LogUtil.log("[${TAG}][${imgFile.name}] do not convert webp because the size become larger!")
}
}
}
将每个文件以.webp结尾,然后调用工具生成。
如果不支持透明通道的png,则进行压缩,压缩的代码在CompressUtil 中。
fun compressImg(imgFile: File) {
if (!ImageUtil.isImage(imgFile)) {
return
}
val oldSize = imgFile.length()
val newSize: Long
if (ImageUtil.isJPG(imgFile)) {
val tempFilePath: String = "${imgFile.path.substring(0, imgFile.path.lastIndexOf("."))}_temp" +
imgFile.path.substring(imgFile.path.lastIndexOf("."))
Tools.cmd("guetzli", "${imgFile.path} $tempFilePath")
val tempFile = File(tempFilePath)
newSize = tempFile.length()
LogUtil.log("newSize = $newSize")
if (newSize < oldSize) {
val imgFileName: String = imgFile.path
if (imgFile.exists()) {
imgFile.delete()
}
tempFile.renameTo(File(imgFileName))
} else {
if (tempFile.exists()) {
tempFile.delete()
}
}
} else {
Tools.cmd("pngquant", "--skip-if-larger --speed 1 --nofs --strip --force --output ${imgFile.path} -- ${imgFile.path}")
newSize = File(imgFile.path).length()
}
LogUtil.log(TAG, imgFile.path, oldSize.toString(), newSize.toString())
}
}
这里的压缩有两种方式,对于JPG格式的则采用guetzli压缩,PNG格式的则采用pngquant压缩。guetzli压缩是无损压缩,压缩时间比较长,压缩率只能到百分之30.而pngquant算法是有损压缩,不过损失度在可接受范围内。
遇到的问题
,部分转换的会把原来png格式的图片删掉,但是还有部分的没有删掉,会同时存在两张不同格式的图片。到mergeDebugResource这一步报错,很多张图片Duplicate resources。
网友评论