为每个 Build Variant 使用不同的 manifestPlaceholder

Posted

技术标签:

【中文标题】为每个 Build Variant 使用不同的 manifestPlaceholder【英文标题】:Using a different manifestPlaceholder for each Build Variant 【发布时间】:2015-10-06 07:34:30 【问题描述】:

首先我要说我对 Gradle 很陌生,所以如果这个问题已经得到解答,我深表歉意。

我正在开发一个使用 API 密钥访问第三方工具的 android 应用程序。根据应用的flavor构建类型,需要使用不同的 API 密钥。

这是我正在尝试做的基本概述:

android 
    defaultConfig 
        manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
    

    buildTypes
        debug
            // Some debug setup
        
        release
            // Some release setup
        
    

    productFlavors 
        // List of flavor options
    
    productFlavors.all flavor->
        if (flavor.name.equals("someFlavor")) 
            if (buildType.equals("release")) 
                manifestPlaceholders = [ apiKey:"RELEASE_KEY_1" ]
             else 
                manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
            
         else 
            if (buildType.equals("release")) 
                manifestPlaceholders = [ apiKey:"RELEASE_KEY_2" ]
             else 
                manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
                
        
    

到目前为止,manifestPlaceholders 语句在一个非常简单的情况下工作,但我不知道如何从 productFlavors 块中引用 buildType 以便我可以将其用作条件。

【问题讨论】:

【参考方案1】:

您可以通过访问特定 applicationVariant 的 mergeFlavor 来在 applicationVariants 中设置 manifestPlaceholders。

android.applicationVariants.all  variant ->
    def mergedFlavor = variant.getMergedFlavor()
    mergedFlavor.manifestPlaceholders = [appPackageId: "myPackageExample"]

如果您使用的是 Kotlin DSL,则应该使用如下内容:

android.applicationVariants.all  // don't put 'variant ->' here or you'll get the 'all' extension function
    // no need to define 'mergedFlavor' because 'this' _is_ the variant so 'mergedFlavor' is already available.
    mergedFlavor.manifestPlaceholders = ...

【讨论】:

这应该是公认的答案,因为它允许您读取不应签入 SCM 的 gradle 配置中的属性文件(应包含您的密钥),然后在清单占位符中设置密钥.通过将密钥放入资源文件中,您可以将它们公开给访问您的存储库的任何人。 这应该是公认的答案。为了区分变体,您可以这样做:android.applicationVariants.all variant -> def mergedFlavor = variant.getMergedFlavor() if (variant.buildType.name.equals("debug") && variant.flavorName.equals("demo")) mergedFlavor.manifestPlaceholders = [appPackageId: "myPackage1"] 无法让它与connected 测试一起使用。适用于其他一切【参考方案2】:

我猜你指的是 Fabric ApiKey? :) 我只是花了几个小时尝试以类似的方式使用占位符并在 gradle 文件中指定 ApiKey,尽管从 com.android.tools.build:gradle:1.3.1 开始似乎不可能。可以为特定风味指定占位符,但不能为风味和 buildType 指定占位符。

只是为了纠正你的语法,你必须这样做(如果可能的话)将是类似的,但 manifestPlaceholders 对变体是未知的。

applicationVariants.all variant->
    if (variant.productFlavors.get(0).name.equals("someFlavor")) 
        if (variant.buildType.name.equals("release")) 
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_1" ]
         else 
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
        
     else 
        if (variant.buildType.name.equals("release")) 
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_2" ]
         else 
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
            
    

您真正需要做的是将密钥保留在AndroidManifest.xml 中并使用多个清单文件处理它

src/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="DEBUG_KEY" tools:replace="android:value"/>
    </application>    
</manifest>

src/someFlavorRelease/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="RELEASE_KEY_1" tools:replace="android:value"/>
    </application>    
</manifest>

src/someOtherFlavorRelease/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="RELEASE_KEY_2" tools:replace="android:value"/>
    </application>    
</manifest>

manifestMerger 将处理替换,您最终将在每种情况下都获得正确的密钥。我刚刚成功实施。我只是希望你真的指的是 Fabric 密钥! :)

希望这会有所帮助!

【讨论】:

感谢您的帖子!实际上,我找到了一种不同的方法来做这件事,因为我的应用程序的结构有点独特,但我确实认为你的方法可能是正确的做事方式。我熟悉 Fabric,但不幸的是,这是为了集成 Localytics,一个非常好的分析平台。再次感谢您的帖子! :) @Stoph 你介意分享你的做法吗?我也在寻找一种方法来处理本地化问题 @NelsonRamirez - 实际上我最终将这项任务交给了另一个开发人员,现在查看代码,看起来他们并没有比我更成功。看起来他们只是使用调试和发布密钥并在调试/发布 buildTypes 中设置 manifestPlaceholder。很抱歉我没有更好的答案。 您可以使用variant.mergedFlavor.manifestPlaceholders = 更新值,而无需像@Morten Holmgaard 回答所建议的那样为每个变体创建AndroidManifest.xml 无法为 com.android.build.gradle.internal.api.ApplicationVariantImpl 类型的对象设置未知属性“manifestPlaceholders”。【参考方案3】:

与接受的答案类似,如果您不想复制清单,则可以使用字符串资源来做到这一点。

例如,如果您有两种口味(口味 1 和口味 2) 您最终会得到以下源集。

app/
  src/
    main/
      res/
         values/strings.xml
    flavor1Release/
      res/
         values/strings.xml
    flavor1Debug/
      res/
         values/strings.xml

    flavor2Release/
       res/
         values/strings.xml
    flavor2Debug/
       res/
         values/strings.xml

然后您可以只使用字符串资源作为您的键值

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">    
    <application>    
        <meta-data
            android:name="io.fabric.ApiKey"
            android:value="@string/apiKey" tools:replace="android:value"/>
    </application>    
</manifest>

将所有键保存在一个位置的进一步优化是将它们全部定义在主源集中的 strings.xml 中。然后让风味/构建源集引用这些。

例如:

<resources>
    <string name="flavor1ReleaseKey">flavor1ReleaseKey</string>
    <string name="flavor1DebugKey">flavor1DebugKey</string>
    <string name="flavor2ReleaseKey">flavor2ReleaseKey</string>
    <string name="flavor2DebugKey">flavor2DebugKey</string>
</resources>

然后在您的每个风味/构建源集中,您只需引用这些键。

flavor1Release/res/values/strings.xml

<resources>
     <string name="apiKey">@string/flavor1ReleaseKey</string>
</resources>

【讨论】:

这看起来对我们有用。感谢您的新答案! 这不起作用,因为它正在抛出 Crashlytics Developer Tools error 不需要复制,查看链接***.com/a/65657119/9315431【参考方案4】:

我在https://azabost.com/android-manifest-placeholders/找到了这个很棒的解决方案

android 
    ...
    buildTypes 
        release 
            ...
            manifestPlaceholders.screenOrientation = "portrait"
        
        debug ...
    

android 
    ...
    flavorDimensions "features"

    productFlavors 

    paid 
        dimension "features"
        manifestPlaceholders.hostName = "www.paid-example.com"
    
    free 
        dimension "features"
        manifestPlaceholders.hostName = "www.free-example.com"
    

【讨论】:

【参考方案5】:

我相信您需要 manifestPlaceHolder 来读取 Java 代码中的值,对吗?如果是这种情况,您已经可以在生成的 BuildConfig.java 中读取 FLAVOR 名称。例如,如果您定义一个名称为 smartphone 的风味,则可以使用 BuildConfig.FLAVOR String 访问该值;然后在你的代码中你可以使用一个简单的if (BuildConfig.FLAVOR.equals("smartphone"))...

但也许您需要读取应用程序的一种配置,即 apiKey。在这种情况下,最好的方法是为每种风味创建一个类或字符串资源;这是给你的link。

【讨论】:

这不是我想要做的完全,但是关于能够访问你的 BuildConfig.java 中的东西的注释是有帮助。我也许可以用它来完成我需要的事情。谢谢! 那么,您能准确解释一下您要做什么吗? 我的理解是 Gradle 会为每个构建类型/产品风格组合创建一个构建变体。我只是想要一种方法来检查 productFlavor 块中的内置类型,以便我可以确定要用于我的 manifestPlaceholder 的硬编码字符串 API 密钥。我不确定这有多清楚,哈哈,所以如果这仍然没有意义,请告诉我。谢谢! 好的。您可以在代码中硬编码,而不是在脚本中硬编码您的 api 密钥。您可以为所需的每种风味或变体定义一个 apiKey。阅读此guide - 为每种风味添加额外的源目录部分。该示例讨论了针对不同风格的不同活动,但您可以应用相同的示例以获得不同的资源或不同的类,甚至不同的清单文件。希望对您有所帮助。 我知道我们已经在为其他文件做类似的事情,所以我可能可以利用它来完成我需要的事情。感谢您的帮助!【参考方案6】:

我所做的是将当前的AndroidManifest.xml 复制到app/src/debug

并在调试清单中更改了密钥:

 <meta-data
            android:name="com.crashlytics.ApiKey"
            tools:replace="android:value"
            android:value="@string/crashlytics_debug" />

app/src/mainManifest 是这样的:

<meta-data
        android:name="com.crashlytics.ApiKey"
        android:value="@string/crashlytics_live" />

【讨论】:

【参考方案7】:

您不需要重复的文件 构建.gradle

productFlavors 
    prod 
        applicationId "com.example.prod"
        dimension "mode"
        manifestPlaceholders = [hostName:"some String"]
    
    dev 
        applicationId "com.example.dev"
        dimension "mode"
        manifestPlaceholders = [hostName:"some String"]
    

清单使用“$hostName”。下面的例子

<meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="$hostName" />

【讨论】:

【参考方案8】:

作为对@Eric 帖子的补充,对于AGP 版本com.android.tools.build:gradle:4.x,此代码为sn-p

applicationVariants.all variant->
    if (variant.productFlavors.get(0).name.equals("someFlavor")) 
        if (variant.buildType.name.equals("release")) 
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_1" ]
         else 
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
        
     else 
        if (variant.buildType.name.equals("release")) 
            manifestPlaceholders = [ apiKey:"RELEASE_KEY_2" ]
         else 
            manifestPlaceholders = [ apiKey:"DEBUG_KEY" ]
            
    


应该更新为

androidComponents 
    onVariants(selector().withBuildType("debug")) 
        manifestPlaceholders.apiKey = "DEBUG_KEY"
    

    onVariants(selector().withBuildType("release")) 
        if(flavorName.equals("someFlavor")) 
            manifestPlaceholders.apiKey = "RELEASE_KEY_1"
        else 
            manifestPlaceholders.apiKey = "RELEASE_KEY_2"
    

【讨论】:

以上是关于为每个 Build Variant 使用不同的 manifestPlaceholder的主要内容,如果未能解决你的问题,请参考以下文章

4.1Android Stuido配置你的Build Variant

4.1Android Stuido配置你的Build Variant

90%不知道的Android Build Variant的使用

使用 glib 和 dbus dbus_g_value_build_g_variant 构建 Maliit 时出错

variant.getAssemble()已过时,已替换为variant.getAssembleProvider()

获取 Build Variant flavorName