在Android上使用不同密钥对Gradle签名口味进行签名

51
我有许多版本的Android应用程序,我希望除了一个版本外,所有版本都使用相同的密钥。有一个版本需要使用不同的密钥。 如何仅覆盖应用程序中的1个版本(但在相同的构建类型中,例如“发布”)的signingConfig?
  • 默认情况下,我希望所有版本都使用主要的发布配置。
  • 我只想覆盖1个版本
  • 我想能够通过单个gradlew assembleRelease命令运行所有发布版本

最后一点很重要,因为我目前有超过120个不同的版本,并且还在增长。为了单独定制每个版本,需要大量的额外工作。


相关的帖子我已经尝试过:

从单一构建类型生成使用不同密钥签名的多个构建

  • 这需要为每个风味进行配置
  • 它似乎无论如何都不使用我的自定义signingConfig

使用gradle签署产品风味

  • 接受的解决方案对我无效
  • 根据评论,这可以通过将buildTypes放在productFlavors中来实现,但我不知道如何做到这一点。

Gradle产品风味上的调试签署配置

总的来说,每个解决方案似乎仍然使用默认的发布配置,而不是我的自定义配置。


我的build.gradle的重要部分如下:

signingConfigs {
    releaseConfig {
        storeFile file('key')
        storePassword "pass"
        keyAlias "alias"
        keyPassword "pass"
    }

    custom {
        storeFile file('custom_key')
        storePassword "pass"
        keyAlias "alias"
        keyPassword "pass"
    }
}

productFlavors {
    apple {
        applicationId "demo.apple"
    }
    banana {
        applicationId "demo.banana"
    }

    // def customConfig = signingConfigs.custom
    custom {
        applicationId "custom.signed.app"
        // signingConfig customConfig
    }
 }


buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
    release {
         signingConfig signingConfigs.releaseConfig
         // productFlavors.custom.signingConfig signingConfigs.custom
    }
}

也许只需要在文档中找到解释这个问题的地方 - 我觉得Gradle的文档并不是特别好。 - Richard Le Mesurier
感谢您提出这个精彩的问题和答案!我看到您现在维护着大约120种口味,构建时间非常长。我也在创建一个白标应用程序,需要多次重新品牌定制。我刚刚开始。虽然我必须为每个口味使用不同的密钥进行签名,但还有其他UI和配置更改。您有什么建议,如何在架构/配置方面维护这样一个庞大的项目,要注意什么,或者如果您现在可以从头开始做些什么不同的事情?如果您有时间分享,这将是一篇很棒的博客文章主题。提前致谢。 - Shobhit Puri
6个回答

30

Gradle插件用户指南中提到,您可以:

 

通过单独设置每个android.productFlavors.*.signingConfig对象,使每个发布包使用自己的SigningConfig

这在此答案(Debug Signing Config on Gradle Product Flavors)和此博客文章(Building Multiple Editions of an Android App with Gradle)中有所体现。

但是,为每个flavor指定单独的signingConfig行并不具备可扩展性,并且超出了问题的范围。不幸的是,提供的答案中没有一个显示如何正确覆盖signingConfig的。


此技巧来自于这个答案(How to get the currently chose build variant in gradle?),其中显示了如何循环遍历构建变体(以及扩展,即flavor)。

我的解决方案使用循环在每个flavor上设置signingConfig,而不是为每个flavor单独指定一行。这很好地扩展了。 "覆盖"是通过在循环后指定自定义配置的单行完成的。

将以下代码放入buildTypes.release块中:

// loop over all flavors to set default signing config
productFlavors.all { flavor ->
    flavor.signingConfig signingConfigs.releaseConfig
}
// override default for single custom flavor
productFlavors.custom.signingConfig signingConfigs.custom

1
很不幸,你必须跳过这么多的障碍才能让它正常工作。按照他们的文档来看,需要迭代变量并通过字符串名称查找口味似乎是相反的。虽然productFlavors.all实际上是一个常见的脚本块,但我不确定你是否可以在那里切换signingConfig。特别是因为他们展示了一个没有这个的例子。但我很高兴你找到了解决方案!谢谢你的回复。 - pjco
1
喜欢这个,这也简化了我现在所拥有的。 - Edison
3
请注意,在 build.gradle 中顺序很重要 -- 必须在 buildTypes {} 之前声明 productFlavors {} 和 signingConfigs {}。否则没有此类顺序要求。 - Johnny C
这不正确,release{} buildType 将覆盖之前声明的 buildType 块,这样只有 release{} 签名密钥将被使用。 - Jemshit Iskenderov
自2015年发布以来,事情可能已经发生了变化 - 欢迎在@Jemshit处发布更新的答案。这个问题和答案来自一个100%的生产项目,每天构建调试和发布版本类型。至少在过去,从我的个人使用经验来看,这个方法是100%可行的。 - Richard Le Mesurier

19

如果在产品风格中未指定签名配置,则下面给出的代码将使用release1作为默认签名配置。

app/build.gradle

signingConfigs {
    debug {
        storeFile file("/home/.../debugkeystore.jks")
        storePassword "..."
        keyAlias "..."
        keyPassword "..."
    }
    release1 {
        storeFile file("/home/.../testkeystore1.jks")
        storePassword "..."
        keyAlias "..."
        keyPassword "..."
    }
    release2 {
        storeFile file("/home/.../testkeystore2.jks")
        storePassword "..."
        keyAlias "..."
        keyPassword "..."
    }
    release3 {
        storeFile file("/home/.../testkeystore3.jks")
        storePassword "..."
        keyAlias "..."
        keyPassword "..."
    }
}

defaultConfig {
    applicationId "com.example.signingproductflavors"
    minSdkVersion 15
    targetSdkVersion 24
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    signingConfig signingConfigs.release1
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    debug { 
        signingConfig signingConfigs.debug
    }
}

productFlavors {
    blocks {
        applicationId "com.example.blocks"
        resValue 'string', 'APP_NAME', "Blocks"
    }
    cloud {
        applicationId "com.example.cloud"
        resValue 'string', 'APP_NAME', "Cloud"
        signingConfig signingConfigs.release2
    }
    deck {
        applicationId "com.example.deck"
        resValue 'string', 'APP_NAME', "Deck"
        signingConfig signingConfigs.release3
    }
}

我喜欢简单优雅的风格,但这样行得通吗?在这种情况下,你如何构建一个带有不同密钥的“debug”版本?我喜欢使用“defaultConfig”的想法,但这样会不会覆盖所有的构建类型? - Richard Le Mesurier
1
是的,这段代码可以工作。产品风味的属性会覆盖默认配置的属性。默认配置不会覆盖构建类型。您可以在buildTypes中使用debug { signingConfig signingConfigs.release3 }来为调试构建类型定义不同的签名配置。 - Ashwin
1
那么代码会是什么样子:1)所有调试构建使用相同的调试签名配置。2)除一个发布 flavor 外,所有其他发布 flavor 使用相同的发布配置。3)单个发布 flavor 使用不同的密钥? - Richard Le Mesurier
1
  1. 所有调试版本都使用相同的调试签名配置。
  2. 所有发布版本可以使用在产品风格中指定的不同发布签名配置。
  3. 如果在其产品风格中未指定任何内容,则发布版本可以使用默认的发布签名配置。
- Ashwin
1
你能否通过更新你的回答来展示一下那段代码是什么样子的? - Richard Le Mesurier
显示剩余5条评论

2
您需要在buildTypes中定义signingconfigs。将自定义签名配置添加到调试的构建类型中,或创建自定义构建类型。
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
             signingConfig signingConfigs.custom
        }
        custom {
            applicationIdSuffix ".custom"
             signingConfig signingConfigs.custom
        }
        release {
             signingConfig signingConfigs.releaseConfig
        }
}

Gradle会为每个构建类型创建一个flavor,并根据buildType使用相应的signinconfig。考虑上述构建类型配置,我们来看看"apple" flavor。Gradle将为apple创建以下构建变体:
  • appledebug --> 自定义签名配置
  • applecustom --> 自定义签名配置
  • applerelease --> 发布签名配置

    您可以选择相应的构建变体并运行应用程序

向flavor添加签名配置

productFlavors {
    def customSigningConfig = signingConfigs.custom

    custom {
        ...
        signingConfig customSigningConfig
        ...
    }

你需要先声明签名配置(signingConfigs),然后再声明产品风味(flavors)。

https://code.google.com/p/android/issues/detail?id=64701


强调一下:我有118种口味使用相同的密钥。我有1种口味使用不同的密钥。我想知道如何使用单个命令让Gradle为所有119种口味构建发布APK文件。 - Richard Le Mesurier
1
118 种口味似乎是相当多的,尽管如此。 - rahul.ramanujam
2
已经有119个了。gradle build 还会构建出119个不必要的调试 APK,以及118个不必要的自定义 APK(因为我只需要一个自定义 APK)。当前构建时间为1个半小时。gradle build 将需要4个半小时。不太好扩展。 - Richard Le Mesurier
1
我已经更新了有关如何在构建风格上设置签名配置的帖子。 - rahul.ramanujam
你测试过那段代码并且让它正常工作了吗?尽管我试了很多次,但我无法让它捕获自定义签名配置。 - Richard Le Mesurier
显示剩余2条评论

1
我不确定这个方法是否有效,但我认为您不想创建新的构建类型。那样会为每种风味创建一个新的构建变体。实际上,您只需要一个风味覆盖“默认配置”即可 :)
这段代码没有经过测试,但您应该能够按照以下方式进行操作:
signingConfigs {
    normal {
        storeFile file('key')
        storePassword "pass"
        keyAlias "alias"
        keyPassword "pass"
    }

    custom {
        storeFile file('custom_key')
        storePassword "pass"
        keyAlias "alias"
        keyPassword "pass"
    }
}

    /**
     *  defaultConfig is of type 'ProductFlavor'.
     *
     *  If we need to use a different signing key than the default,
     *  override it in the specific product flavor.
     */

    defaultConfig {
        versionCode 123
        versionName '1.2.3'
        minSdkVersion 15

        def standardSigningConfig = signingConfigs.normal

        buildTypes{
            release {
               signingConfig standardSigningConfig 
               zipAlign true
               // ...
            }
            debug { 
               //not sure you need this node
            }  
        }
    }


productFlavors {

    def customConfig = signingConfigs.custom
    def standardSigningConfig = signingConfigs.normal

    apple {
        applicationId "demo.apple"
    }
    banana {
        applicationId "demo.banana"
    }
    custom {
        applicationId "custom.signed.app"
        signingConfig customConfig
    }
}

Reference:
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Product-Flavor-Configuration


感谢提供文档链接。为了获得可行的解决方案,这是一次相当艰辛的旅程。 - Richard Le Mesurier

1

一个想法是使用项目属性来确定是否应该使用自定义的signinconfig。

if (project.hasProperty('custom')) {
    android.signingConfigs.release = customSigningConfig
} else {
    //should use the default
}

然后,要构建您的自定义版本,请运行:
gradle assembleCustomRelease -Pcustom=true

但是这个调用只会构建您特定的变体,最多只需要几分钟时间。它也可以被脚本化和/或集成到您的CI中,因此您不需要任何努力。 - Arnaud
感谢@arnaud,谢谢。然而,这个问题是寻找一种方法来覆盖单个口味/变体的“signingConfig”,以避免使用额外的命令或脚本。 - Richard Le Mesurier

0

简而言之,通过“gradle.startParameter.taskNames”查找口味并修改变量。

我为Vine应用程序的测试变体执行此操作,效果非常好。您还可以使用此方法使不同的依赖项编译,而无需添加更多的口味维度。

在您的情况下,它可能看起来像这样。

            //root of buil.gradle OR probably inside buildTypes.release
            def signType = signingConfigs.normal;
            //You can put this inside builTypes.release or any task that executes becore
            def taskNames = gradle.startParameter.taskNames;
                taskNames.each { String name ->
                    if (name.contains("customFlavor")) {
                        signType = signingConfigs.custom
                    }
                }
            buildTypes{
                release {                   
                    signingConfig signType
                }
            }

当运行 gradlew assembleRelease 时,taskNames 只包含 "assembleRelease" 而不是所有的 flavors。因此这并没有什么有用的作用。 - Richard Le Mesurier
是的,那只是一个占位符。 - Edison
让我看看能否找到当前任务名称。 - Edison

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