qq549631030 / AndroidJunkCode

Android马甲包生成垃圾代码插件
1.09k stars 252 forks source link

manifestPlaceholders配置导致编译失败 #30

Closed developerzjy closed 2 years ago

developerzjy commented 2 years ago

项目中存在多个productFlavors和buildTypes,对于manifestPlaceholders,要求不同的flavor使用不同appKey,同一个flavor的不同bulidType也要使用不同的appKey

在实现上述配置后,加入junk库编译不通过,错误原因是:Manifest merger failed

详情如下

我的配置如下(加上了print打印):

android {
    productFlavors {
        flavorA {
            println 'zjyzjy 1.productFlavors'
        }
        flavorB {
        }

        //此处配置flavorADebug, flavorARelease, flavorBDebug, flavorBRelease分别使用不同的appKey
        productFlavors.all { flavor ->
            applicationVariants.all { variant ->
                def mergedFlavor = variant.mergedFlavor
                switch (variant.flavorName) {
                    case "flavorA":
                        switch (variant.buildType.name) {
                            case "debug":
                                mergedFlavor.manifestPlaceholders += [appKey: "1111"]
                                break
                            case "release":
                                mergedFlavor.manifestPlaceholders.replace("appKey", "2222")
                                println 'zjyzjy 4.productFlavors.all'
                                break
                        }
                        break
                    //case "flavorA" 省略
                }
            }
        }
    }

    buildTypes {
        debug {
        }
        release {
            println 'zjyzjy 2.buildTypes: '
        }
    }
}

androidJunkCode {
    //...省略代码
    variantConfig {
        println 'zjyzjy 3.androidJunkCode: '
        kaituanRelease config
    }
}

print打印如下

zjyzjy 1.productFlavors
zjyzjy 2.buildTypes: 
zjyzjy 3.androidJunkCode:  //这时可能触发了manifest合并导致编译失败
zjyzjy 4.productFlavors.all:  //这里才开始根据flavor和buildType添加appkey
zjyzjy 4.productFlavors.all: 

报错如下

Task :app:processFlavorAReleaseMainManifest FAILED E:\xxx\xxx\xxx\app\src\main\AndroidManifest.xml:464:13-43 Error: Attribute meta-data#appKey@value at AndroidManifest.xml:464:13-43 requires a placeholder substitution but no value for is provided. E:\xxx\xxx\xxx\app\build\generated\source\junk\FlavorARelease\AndroidManifest.xml Error: Validation failed, exiting

qq549631030 commented 2 years ago

你这配置有很多错误的地方: productFlavors.all { flavor -> // 这一层不需要 } 你报错的根本原因是这里不应该用replace,要用+= ["appKey", "2222"] mergedFlavor.manifestPlaceholders.replace("appKey", "2222")

developerzjy commented 2 years ago

你这配置有很多错误的地方: productFlavors.all { flavor -> // 这一层不需要 } 你报错的根本原因是这里不应该用replace,要用+= ["appKey", "2222"] mergedFlavor.manifestPlaceholders.replace("appKey", "2222")

一、我觉得根本原因是gradle中执行顺序的问题,执行顺序如下(通过print打印来看的):

zjyzjy 1.productFlavors  //第1步先执行productFlavors 块
zjyzjy 2.buildTypes:  //第2步执行buildTypes块
zjyzjy 3.androidJunkCode:  //第3步执行androidJunkCode触发了manifest合并,但是此时还没配置appKey
zjyzjy 4.productFlavors.all:  //第4步执行这里,添加appKey配置(此时才能拿到buildType和flavors)

二、根据上面的执行顺序,和你提出配置的错误:

1.productFlavors.all { flavor -> } 这一层我试了一下,加不加结果都一样,无法改变执行顺序 2.关于replace,我本来是用的+=,接入junk库后编译失败了,我按照执行顺序尝试了以下解决方法: (1)在buildType中先配置好manifestPlaceholders = [appKey: "xxx"],保证执行到androidJunkCode的时候先不报错 (2)执行到productFlavors.all的时候使用replace看看能否将buildType中配置的appKey替换成正确的appKey 我尝试的解决方法是不可行的(replace和+=都是不行的)

三、我想到的可能的解决方案是否可行

1.是否能改变第3步和第4步的执行顺序 2.能否实现productFlavors.all替换buildType的appKey 3.能否在androidJunkCode块中执行flavorARelease config之前添加appKey 4.其他没想到的方案

以上我想到的几种方案,还不知道能不能实现或者怎么实现,作者大佬能否给些意见

qq549631030 commented 2 years ago

//这样打印看你的配置有没有生效 afterEvaluate { android.applicationVariants.all { variant -> def mergedFlavor = variant.mergedFlavor print(variant.name + ": ") println(mergedFlavor.manifestPlaceholders) } }

developerzjy commented 2 years ago

加junk的前提肯定是项目已经全部跑通了,渠道配置、业务代码都已经测试没问题了。然后才准备接入junk库,但是接入的时候编译失败。

这两天学习了一下gradle插件,然后看了一下你的代码,问题已经初步解决了

我觉得源码中的以下代码可能会有问题(我对gradle不是很熟,可能观点是错误的)

//将自动生成的AndroidManifest.xml加入到一个未被占用的manifest位置(如果都占用了就不合并了,通常较少出现全被占用情况)
        for (int i = variant.sourceSets.size() - 1; i >= 0; i--) {
            def sourceSet = variant.sourceSets[i]
            if (!sourceSet.manifestFile.exists()) {
                android.sourceSets."${sourceSet.name}".manifest.srcFile(manifestFile)
                def processMainManifestTask = project.tasks.findByName("process${variantName.capitalize()}MainManifest")
                if (processMainManifestTask) {
                    processMainManifestTask.dependsOn(generateJunkCodeTask)
                }
                break
            }
        }

插件是以上面这种方式将生成的manifest合并到最终的manifest中的,但是这样改变了默认sourceSet的manifest路径,不知道对Android编译的自带task是否会有影响,最终导致manifest合并失败

> Task :app:generateFlavorADebugJunkCode  //junk task
> Task :app:generateFlavorADebugResValues
> Task :app:generateFlavorADebugResources //凭感觉怀疑可能对这个task有影响

网上一个关于sourceSets的描述:

gradle的sourceSets可以对不同的buildType, productFlavor,buildVariant设置不同的文件路径,进行多样化处理。 一个工程项目里多个module或者多个buildType,productFlavor情况下,最后打包成一个apk,相关文件如何挑选打包进apk呢? 如果配置的文件目录是针对资源文件,即:AndroidManifest和values目录下的文件,则相当于是一个额外的配置。打包的时候是会进行资源文件上的合并

于是修改如下: 1.注释掉junk源码中我上面怀疑的那段for循环代码 2.app的gradle中加入如下代码

sourceSets {
        falvorADebug {
            manifest.srcFile 'build/generated/source/junk/falvorADebug/AndroidManifest.xml'
        }
    }

以上相比于改之前的junk库,多了一步sourceSets配置,麻烦了点,作者大佬有更优雅的修改方式可以交流一下

qq549631030 commented 2 years ago

了解了,下个版本修复

qq549631030 commented 2 years ago

已修复,请升级到1.1.3版本