使用Gradle为产品口味签名

86

我正在尝试将我的项目迁移到Gradle。我的其中一个项目有多个产品风味,每个产品风味的发布版本都必须使用不同的signingConfig进行签名。到目前为止,这是我尝试过的:

buildscript {
    ...
}

apply plugin: 'android'

android {
    compileSdkVersion 17
    buildToolsVersion '17'

    signingConfigs {
        flavor1 {
            storeFile file("keystore")
            storePassword "secret"
            keyAlias "aliasForFlavor1"
            keyPassword "secretFlavor1"
        }

        flavor2 {
            storeFile file("keystore")
            storePassword "secret"
            keyAlias "aliasForFlavor2"
            keyPassword "secretFlavor2"
        }
    }

    productFlavors {
        flavor1 {
            signingConfig signingConfigs.flavor1
        }

        flavor1 {
            signingConfig signingConfigs.flavor2
        }
    }
}

dependencies {
    ...
}

当我运行gradle build时,我会得到一个groovy.lang.MissingFieldException和以下错误消息:

No such field: signingConfigs for class: com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory

所以我认为在Gradle脚本中的productFlavors.*部分不是放置代码签名配置的正确位置。


该解决方案在此处运行良好:https://dev59.com/7V0Z5IYBdhLWcg3w8j9J#40124853 - ultraon
对于那些询问为什么“debug”构建未选择风味配置的相关问题,这里有一个相关的问题。 https://dev59.com/LV0b5IYBdhLWcg3wGN0s - Richard Le Mesurier
6个回答

112
你可以为每个flavorbuildType中声明签名配置。这是我用于发布签名不同密钥库的gradle文件。

你可以为每个flavorbuildType中声明签名配置。这是我用于发布签名不同密钥库的gradle文件。

android {
  signingConfigs {
    configFirst {
        keyAlias 'alias'
        keyPassword 'password'
        storeFile file('first.keystore')
        storePassword 'password'
    }

    configSecond {
        keyAlias 'alias'
        keyPassword 'password'
        storeFile file('second.keystore')
        storePassword 'password'
    }
  }

  compileSdkVersion 23
  buildToolsVersion "23.0.2"
  defaultConfig {
        minSdkVersion 14
        targetSdkVersion 23
  }

  productFlavors{
    flavor1 {
        applicationId "com.test.firstapp"
    }

    flavor2 {
        applicationId "com.test.secondapp"
    }
  }

  buildTypes {
    release {
        productFlavors.flavor1.signingConfig signingConfigs.configFirst
        productFlavors.flavor2.signingConfig signingConfigs.configSecond           

        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'

    }
  }
}

buildTypes块应该放在productFlavors块之后,我的意思是顺序很重要。


1
@almaz_from_kazan 我最初也是这样做的,使用了 debug {productFlavors.flavor1.signingConfig signingConfigs.configFirst} 但它没有识别出 signingConfigs,所以我改用了 debug.initWith(buildTypes.release) debug {} 而不使用 productFlavors.flavor1.signingConfig signingConfigs.configFirst,现在调试版本的签名已经按预期工作了。谢谢! - mtsahakis
31
Android为调试版本增加了默认的signingConfig。要通过传递null来删除它,就像这样:buildTypes { debug { signingConfig null } }。这样做可以将signingConfig委托给产品风格。 - Allan Hasegawa
10
好的答案,值得注意的是,buildTypes必须在productFlavors之后。 - EpicPandaForce
1
工作得很好...请确保从您的发布配置中删除“signingConfig signingConfigs.release”,否则其他配置将无法覆盖它...我以为它们会覆盖此配置,并且我可以将其用作默认值。 - kenyee
1
这个能行吗?如果我之后迭代applicationVariants,所有的构建类型都会有相同的签名配置,这些配置是在最后一个构建类型中指定的。 - Simas
显示剩余5条评论

55
根据用户指南,Flavor的签名配置是被支持的。
这里的问题与signingConfigs对象的范围有关。我只是把它分配给了productFlavors块中的一个变量,但是在flavor1块之外,以解决这个问题。
productFlavors {
    def flavor1SigningVariable = signingConfigs.flavor1

    flavor1 {
        ...
        signingConfig flavor1SigningVariable
        ...
    }

6
这其实是没有必要的。我提交了https://code.google.com/p/android/issues/detail?id=64701来跟踪此问题。 - Xavier Ducrohet
3
Gradle v2.4中似乎出现了问题,解决方案似乎已经失效。 - Richard Le Mesurier
3
在我的情况下,当我添加android.buildTypes.debug.signingConfig = null;和 android.buildTypes.relese.signingConfig = null; 来清除基本签名后,它起作用了。这意味着buildTypes中的signingConfig会覆盖一个flavor中的signingConfig,甚至是Android自动创建的默认签名配置。例如,你可以通过打印android.buildTypes.debug.signingConfig来进行检查。 - Alexey
1
谢谢您的android.buildTypes.debug.signingConfig = null;,直到我看到这个小注释,我浪费了整整一个下午。也许值得将其添加到主要响应中。 - casolorz
@Alexey,你的评论救了我的命。使用Gradle KTS风格签名一直被忽略。使用普通Gradle相同的配置没有问题。但是在Gradle KTS中,需要首先重置构建类型的签名配置android.buildTypes.relese.signingConfig=null。我猜这是一个bug。 - pinkfloyd
显示剩余2条评论

9
安卓的gradle插件仅支持按构建类型签名,不支持按风格签名。原因是给定的变体(构建类型+风格)只能由一个密钥签名,但可以是多个风格组合。例如,您的风格组可能是cpu(x86 / arm)和版本(免费/付费),就有四个不同的变体在这里。
您需要的解决方案是为不同的发布版本创建单独的构建类型。例如,您的构建类型可能是调试、发布、发布测试版,就像这样:
...

android {

    ...

    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }

        release {
            signingConfig signingConfigs.release
        }

        release-beta {
            initWith release
            signingConfig signingConfigs.release-beta
        }
    }
}

initWith 上面的代码告诉 Gradle,release-beta 应该是 release 构建类型的副本,只是使用不同的密钥进行签名。


3
您实际上可以将“buildTypes”放在“productFlavors”中(不确定这是否是新功能)。 - pjco
@pjco 把 buildTypes 放在 productFlavors 里面听起来像是一个有帮助的解决方案,可以解决我的这个问题 - https://dev59.com/7V0Z5IYBdhLWcg3w8j9J - 如果你有时间看一下的话。 - Richard Le Mesurier
@pjco 是的,我这样做了 builtTypes{ release {productFlavors.pro.versionCode 1} } - Jemshit Iskenderov

7

也许另一种有趣的解决方案,具有动态风味签名配置和其他优势

  • 我很喜欢在Gradle中定义风味的应用程序ID和名称(对于每个风味仅需2行代码,非常清晰),但我不想定义单独的签名配置(添加风味时Gradle文件会变得过长)。
  • 由于提交时的问题,我也不希望将敏感的签名信息放置在Gradle文件中。
  • 额外的好处是调试版本有另一个应用程序ID和名称。

.gitignore

...
/keystore.properties

.keystore.properties

storeFile=../mystore.jks
storePassword=...

keyAliasFlavor1=...
keyPasswordFlavor1=...

keyAliasFlavor2=...
keyPasswordFlavor2=...

app/build.gradle

def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(rootProject.file('keystore.properties')))

android {
    ...

    buildTypes {
        debug {
            ...
            manifestPlaceholders = [appNameSuffix: " Dev"]
            applicationIdSuffix ".dev"
        }
        release {
            ...
            manifestPlaceholders = [appNameSuffix: ""]
            productFlavors.all { flavor ->
                flavor.signingConfig = android.signingConfigs.create("${flavor.name}")
                flavor.signingConfig.storeFile = rootProject.file(keystoreProperties["storeFile"])
                flavor.signingConfig.storePassword = keystoreProperties["storePassword"]
                flavor.signingConfig.keyAlias = keystoreProperties["keyAlias${flavor.name}"]
                flavor.signingConfig.keyPassword = keystoreProperties["keyPassword${flavor.name}"]
            }
        }
    }

    productFlavors {
        Flavor1 {
            applicationId "..."
            manifestPlaceholders = [appNameBase: "MyApp 1"]
        }
        Flavor2 {
            applicationId "..."
            manifestPlaceholders = [appNameBase: "MyApp 2"]
        }
        // ... and many other flavors without taking care about signing configs
        // (just add two lines into keystore.properties for each new flavor)
    }
}

app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application android:label="${appNameBase}${appNameSuffix}" ...>
        ...
    </application>
</manifest>

1
动态创建signingConfig的部分正是我所寻找的!谢谢。 - Tom Curran
1
正是我所需要的。我们使用它来在GMS和HMS之间分割我们的签名配置。productFlavors.all { flavor -> if (flavour.name.toLowerCase() == "gms") { signingConfig signingConfigs.debug } if (flavour.name.toLowerCase() == "hms") { signingConfig signingConfigs.releaseHms } } - Francois

2
这是Kotlin DSL的等价物,相当于ashakirov的答案
// See https://dev59.com/_FIH5IYBdhLWcg3wOrEY
fun getLocalProperty(key: String) = gradleLocalProperties(rootDir).getProperty(key)
fun String?.toFile() = file(this!!)
// Could also use System.getenv("VARIABLE_NAME") to get each variable individually
val environment: Map<String, String> = System.getenv()

android {
    signingConfigs {
        create("MyFirstConfig") {
            keyAlias = getLocalProperty("signing.keyAlias") ?: environment["SIGNING_KEY_ALIAS"]
            storeFile = (getLocalProperty("signing.storeFile") ?: environment["SIGNING_STORE_FILE"]).toFile()
            keyPassword = getLocalProperty("signing.keyPassword") ?: environment["SIGNING_KEY_PASSWORD"]
            storePassword = getLocalProperty("signing.storePassword") ?: environment["SIGNING_STORE_PASSWORD"]
            enableV1Signing = true
            enableV2Signing = true
        }
        create("MySecondConfig") {
            keyAlias = getLocalProperty("signing.keyAlias2") ?: environment["SIGNING_KEY_ALIAS2"]
            storeFile = (getLocalProperty("signing.storeFile2") ?: environment["SIGNING_STORE_FILE2"]).toFile()
            keyPassword = getLocalProperty("signing.keyPassword2") ?: environment["SIGNING_KEY_PASSWORD2"]
            storePassword = getLocalProperty("signing.storePassword2") ?: environment["SIGNING_STORE_PASSWORD2"]
            enableV1Signing = true
            enableV2Signing = true
        }
    }

    productFlavors {
        create("flavor1") {
            // ...
        }
        create("flavor2") {
            // ...
        }
    }

    buildTypes {
        getByName("release") { // OR simply  release {  in newer versions of Android Gradle Plugin (AGP)
            productFlavors["flavor1"].signingConfig = signingConfigs["MyFirstConfig"]
            productFlavors["flavor2"].signingConfig = signingConfigs["MySecondConfig"]
            // OR alternative notation
            // productFlavors {
            //     getByName("flavor1") {
            //         signingConfig = signingConfigs["MyFirstConfig"]
            //     }
            //     getByName("flavor2") {
            //         signingConfig = signingConfigs["MySecondConfig"]
            //     }
            // }
        }
    }
}

0

GmsHms构建之间拆分签名配置

如果有人需要在GmsHms构建之间拆分签名配置,可以参考以下内容。

这是对此处列出的答案的补充:
也许还有一种有趣的解决方案,具有动态风味签名配置和其他优点


build.gradle

选项1

gradle.startParameter.getTaskNames().each()

   def keystorePropertiesFile = rootProject.file("keystore.properties")
   def keystoreProperties = new Properties()
   keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

   signingConfigs {
        release {
            storeFile file(keystoreProperties["RELEASE_STORE_FILE"])
            storePassword keystoreProperties["RELEASE_STORE_PASSWORD"]
            keyAlias keystoreProperties["RELEASE_KEY_ALIAS"]
            keyPassword keystoreProperties["RELEASE_KEY_PASSWORD"]
        }
        releaseHms {
            storeFile file(keystoreProperties["RELEASE_HMS_STORE_FILE"])
            storePassword keystoreProperties["RELEASE_STORE_PASSWORD"]
            keyAlias keystoreProperties["RELEASE_KEY_ALIAS"]
            keyPassword keystoreProperties["RELEASE_KEY_PASSWORD"]
        }
        debug {
            // use default debug key to sign
        }
    }
    
   buildTypes {
        release {
            ...

            gradle.startParameter.getTaskNames().each { task ->
                if (task.toLowerCase().contains("gms")) {
                    signingConfig signingConfigs.release
                }
                if (task.toLowerCase().contains("hms") {
                    signingConfig signingConfigs.releaseHms
                }
            }
        }

        debug {
            ...

            gradle.startParameter.getTaskNames().each { task ->
                if (task.toLowerCase().contains("gms")) {
                    signingConfig signingConfigs.debug
                }
                if (task.toLowerCase().contains("hms") {
                    signingConfig signingConfigs.releaseHms
                }
            }

        flavorDimensions "serviceplatform"
        productFlavors {
        hms {
            dimension "serviceplatform"
            applicationIdSuffix ".huawei"
            versionNameSuffix "-huawei"
        }
        gms {
            dimension "serviceplatform"
            applicationIdSuffix ".android"
        }
     }

   sourceSets {
        main {
            res.srcDirs = [
                    "src/main/res",
                    "src/main/res/layout/toolbar",
                    "src/main/res/layout/fragment",
                    "src/main/res/layout/activity"
            ]
        }

        gms {
            java.srcDir("src/gms/java")
        }
        hms {
            java.srcDir("src/hms/java")
        }
    }

选项2

productFlavors.gms.signingConfig

请确保您的flavorDimensions位于buildTypes之前

flavorDimensions "serviceplatform"
     productFlavors {
        hms {
            ...
        }
        gms {
            ...
        }
    }

buildTypes {
        release {
            ...
            productFlavors.gms.signingConfig signingConfigs.release
            productFlavors.hms.signingConfig signingConfigs.releaseHms
        }

        debug {
            ...
            productFlavors.gms.signingConfig signingConfigs.debug
            productFlavors.hms.signingConfig signingConfigs.releaseHms
        }
    }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接