AGP8.0+ 中 如何处理mergeResources任务的产物

587 阅读1分钟

在以前的agp版本中,我们可以onVarint回调中直接获取到任意task的provider,这让我们可以很轻松的插入一个任务到android的编译过程中

在8.0+以后 这个方案失效了, 这个会导致我们很多自定义的插件无法执行,举个例子,在编译中将png和jpg 压缩成webp 来缩减包大小是通用方案,但这个方案的第一步 是 你得在mergeRes任务之后 processRes任务之前 处理好这些 res文件

下面介绍一种简单的方案 可以在8.0+ 上实现 这个任务插入的过程

参考2bab大佬的开源实现,注意切换分支到agp-8.1

先拿到provider


class CreationAction(private val appVariant: ApplicationVariant) {
    fun extractMergedRes(): Provider<Directory> {
        return appVariant.getArtifactsImpl()
            .get(InternalArtifactType.MERGED_RES)
    }
}


fun ApplicationVariant.getArtifactsImpl() = getApplicationVariantImpl().artifacts
fun ApplicationVariant.getApplicationVariantImpl(): ApplicationVariantImpl {
    return when (this) {
        is ApplicationVariantImpl -> {
            this
        }

        is AnalyticsEnabledApplicationVariant -> {
            this.delegate as ApplicationVariantImpl
        }

        else -> {
            throw UnsupportedOperationException("Can not convert $this to ApplicationVariantImpl.")
        }
    }
}

然后处理varint和provider的对应关系

val resVariantMap = mutableMapOf<String, Provider<Directory>>()
 val androidComponents = target.extensions.getByType(AndroidComponentsExtension::class.java)
            androidComponents.onVariants { variant ->
                resVariantMap[variant.name] = CreationAction(variant as ApplicationVariant).extractMergedRes()
                }

最后利用doLast来处理

val regex = "^merge|Resources$".toRegex()
target.gradle.taskGraph.whenReady {
    allTasks.forEach {
         // 先找到mergeResource这个任务
        if (it.name.startsWith("merge") && it.name.endsWith("Resources")) {
            it.doLast {
                // 任务的完整名称中间就是varint的名称    
                val varintKey = regex.replace(it.name, "")
                // 我们拿到varint的名称就可以去拿到这个varint对应的mergeResource的路径了
                val result = resVariantMap[varintKey]
                println("{result!!.get().asFile.absolutePath}")
                result.get().asFileTree.files.forEach { info->
                    // 在这里能拿到全部res下的文件 自行处理你的需求即可
                    println("info info = ${info.name}")

                }


            }
        }

整体方案虽然不够优雅 但是基本能完成适配的需求