使用 gradle 签署产品风味

Posted

技术标签:

【中文标题】使用 gradle 签署产品风味【英文标题】:Signing product flavors with gradle 【发布时间】:2013-06-07 02:08:47 【问题描述】:

我很想将我的项目迁移到 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.* 部分不是放置代码签名配置的正确位置。

【问题讨论】:

解决方案在这里运行良好:***.com/a/40124853/3256989 【参考方案1】:

您可以为buildType 中的每个flavor 声明signing config。这是我的 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 块之后,我的意思是顺序很重要。

【讨论】:

@almaz_from_kazan 我最初是这样做的,有一个debug productFlavors.flavor1.signingConfig signingConfigs.configFirst,但它没有接收到signingConfigs,所以我在没有productFlavors.flavor1.signingConfig signingConfigs.configFirst 的情况下切换到debug.initWith(buildTypes.release) debug ,并且签名现在可以按预期进行调试构建。谢谢! Android 为调试版本添加了默认的signingConfig。通过传递null 将其删除,如下所示:buildTypes debug signingConfig null 。通过这样做,您将signingConfig 委托给产品风味。 好答案,值得注意的是buildTypes 必须在 productFlavors之后。 运行良好...确保从您的发布配置中删除“signingConfig signingConfigs.release”,否则其他配置不会覆盖它...我认为他们会覆盖它,我可以使用它作为默认值。 这还能用吗?如果我之后迭代 applicationVariants,所有构建类型都具有在最后一个构建类型中指定的相同签名配置。【参考方案2】:

根据user guide,支持flavors 的signingConfigs。

这里的问题与 signingConfigs 对象的范围有关。我只是将它分配给 productFlavors 块内的变量,但在 flavor1 风味块之外解决问题:

productFlavors 
    def flavor1SigningVariable = signingConfigs.flavor1

    flavor1 
        ...
        signingConfig flavor1SigningVariable
        ...
    

【讨论】:

这真的不应该是必需的。我提交了code.google.com/p/android/issues/detail?id=64701 来跟踪这个。 解决方案似乎在 Gradle v2.4 中被破坏 在我的情况下,添加 android.buildTypes.debug.signingConfig = null;和 android.buildTypes.relese.signingConfig = null;清除基础签名。这意味着 buildTypes 中的signingConfig 会以某种方式覆盖signingConfig。甚至是 Android 自动创建的默认值。例如,您可以通过打印 android.buildTypes.debug.signingConfig 来检查它 如果你想用两种不同的签名配置来获得这种风格怎么办?发布/调试?我认为这种方式更优雅:***.com/a/35057525/2557258 感谢android.buildTypes.debug.signingConfig = null;,我浪费了整个下午才看到那条小评论。可能值得将其添加到主要响应中。【参考方案3】:

Android 的 gradle 插件仅支持按构建类型签名,而不支持按风味签名。这样做的原因是任何给定的 variant(构建类型 + 风味)只能由一个密钥签名,但可以是多个风味组的组合。例如,您的风味组可以是 cpu (x86/arm) 和版本(免费/付费),这就是四种不同的变体。

您正在寻找的解决方案是为您的不同发行版本创建单独的构建类型。例如,您的构建类型可能是debugreleaserelease-beta,如下所示:

...

android 

    ...

    buildTypes 
        debug 
            signingConfig signingConfigs.debug
        

        release 
            signingConfig signingConfigs.release
        

        release-beta 
            initWith release
            signingConfig signingConfigs.release-beta
        
    

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

【讨论】:

您实际上可以将buildTypes 放在productFlavors 中(不过我不确定这是否是新事物) @pjco 将 buildTypes 放入 productFlavors 听起来像是我的这个问题的有用解决方案 - ***.com/q/30898611/383414 - 如果你有时间看看 @pjco 是的,我做到了builtTypes release productFlavors.pro.versionCode 1 【参考方案4】:

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

我可以在 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>

【讨论】:

动态创建signingConfig正是我想要的!谢谢 正是我需要的。我们使用它在 GMS 和 HMS 之间拆分我们的签名配置 ---> productFlavors.all flavor -> if (flavour.name.toLowerCase() == "gms") signingConfig signingConfigs.debug if (flavour.name. toLowerCase() == "hms") signingConfig signingConfigs.releaseHms 【参考方案5】:

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

确保您的 flavorDimensionsbuildTypes 之前

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
        
    

【讨论】:

【参考方案6】:

这是 ashakirov 答案的 Kotlin DSL 等价物:

// See https://***.com/q/60474010
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") 
            productFlavors["flavor1"].signingConfig = signingConfigs["MyFirstConfig"]
            productFlavors["flavor2"].signingConfig = signingConfigs["MySecondConfig"]
            // OR alternative notation
            // productFlavors 
            //     getByName("flavor1") 
            //         signingConfig = signingConfigs["MyFirstConfig"]
            //     
            //     getByName("flavor2") 
            //         signingConfig = signingConfigs["MySecondConfig"]
            //     
            // 
        
    

【讨论】:

以上是关于使用 gradle 签署产品风味的主要内容,如果未能解决你的问题,请参考以下文章

在 gradle 中设置 applicationId 以获得组合的产品风味

Android Gradle flavor —— 打造不同风味的app

Android Gradle flavor —— 打造不同风味的app

Android风味,Gradle sourceSets合并

Android Studio gradle 风味维度构建变体无法正常工作

在android studio中build.gradle中使用flavor维度