App extreme slimming: automatic conversion of png to webp before packaging. It's cool to be as light as a swallow!

Preface

Everyone knows that png takes up the volume of the App. Is there a tool that can automatically convert all png images before packaging (such as assembleDebug, assembleRelease), including those in third-party dependent libraries? I inadvertently found a artifact cwebp conversion before. Tool , can you learn from this tool to write a Plugin to complete the picture conversion, and at the same time support to check large pictures, the picture size is configurable. Don't say much, just do it~

Author: small wooden box
link: https: //juejin.cn/post/6897894068068876295

Before writing a plug-in, you need to think about several business pain points

    1. How to get all the res resources?
    1. When is the execution timing of the automated conversion tool Task?
    1. How to check the big picture, configure the picture size, and automatically turn on the picture conversion switch?

In view of question 1, we can refer to McImage , it is actually very simple, it is a Gradle API, just look at the documentation of the linked document

In view of question 2, the execution timing of the Task actually depends on the MergeResources Task

In view of question 3, we can customize the API setting switch through the Gradle API, the maximum size of the picture, and add a whitelist to the picture

convert2WebpConfig{
    enableWhenDebug true
    maxSize 1024*1024 // 1M
    whiteList ["xxx.png","xxx.png"]
    //...
}

Picture format conversion development process

Step 1: Create a new Gradle Plugin project

Step 2: Add Png to Webp configuration

Step 3: Configure Plugin for com.android.application and com.android.library

Realize custom attribute picture converter switch configuration, picture maximum size configuration, picture add whitelist configuration

Step 5: Move the Mac version and Windows version image conversion tool to the tool/cwebp directory, and add an executable program

The sixth step is to add auto.service to facilitate dynamic addition of dependencies during compilation

    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"

Use AutoService annotations, use reflection to instantiate objects VariantProcessor, dynamically register Convert2WebpTask tasks, and later use the annotation processor to process Convert2WebpTask tasks

@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)
    }
}

The seventh step Convert2WebpTask task execution

7.1 Check whether there is a webp tool in the tools path

7.2 If the configuration attribute configuration switch is false, interrupt the task

      if (!config.enableWhenDebug) {
                return@all
            }

7.3 Get all the Android resource files, traverse the resource files, and add the big picture that meets the conditions to the big picture list

​    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)
​        }
​    }

7.4 Filter out non-compliant image files

  • If the image whitelist is added or the file is not in image format, filter

  • If the picture size is compliant and the picture is a large picture, there is no picture in the white list of the large picture, add it to the large picture list

  • Otherwise add to the picture catalog

    */**     \* 过滤图片     \*/*
    **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)
    }

7.5 Check the big picture and find out the picture quote

   **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())
      }
  }

7.6 Handling image compression tasks

​    **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)
​    }

7.7 Calculate the size of the image before and after compression, and the compression time

 **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("------------------------------------")

to sum up

This article is mainly to do a full picture replacement for the App before packaging. The picture conversion method uses Google's open source tool cwebp. Of course, we can standardize the picture size and plug-in switch by whitelisting. If you master the content of this article, you will not only Your company's application of weight loss is helpful, and it can also make up for your desire for Gradle Plugin knowledge~

notes

[360°all-round performance tuning]

In this note, I have integrated the knowledge points of Android-360° all-round performance optimization, as well as the practical experience of hundreds of millions of user apps such as WeChat, Taobao, Douyin, Toutiao, Gaode Maps, Youku, etc. in performance optimization. A set of system knowledge notes PDF, from theory to practice, involving all the knowledge points of Android performance optimization, up to 721 pages of e-book! I believe that after reading this document, you will have a more systematic and in-depth understanding of the Android performance tuning knowledge system and various solutions.

You can download it directly for free on my GitHub after you like + follow .

End of sentence

Thank you everyone for following me, sharing Android dry goods, and exchanging Android technology.
If you have any insights on the article, or any technical questions, you can leave a message in the comment area to discuss, and I will answer you religiously.
Everyone is also welcome to come to my B station to play with me. There are video explanations of the advanced technical difficulties of various Android architects to help you get promoted and raise your salary.
Through train at station B: https://space.bilibili.com/544650554

Guess you like

Origin blog.csdn.net/Androiddddd/article/details/110491156