为每个构建变体使用不同的manifestPlaceholder

33

首先声明,我对Gradle非常陌生,如果这个问题已经有答案,我表示歉意。

我正在开发一款Android应用程序,使用API密钥来访问第三方工具。根据应用程序的flavorbuild type,需要使用不同的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,以便我可以将其用作条件。

8个回答

49
您可以通过访问特定应用程序变体的mergedFlavor,在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 = ...
}

2
这应该是被接受的答案,因为它允许您在Gradle配置中读取属性文件(其中应包含您的密钥),这些文件不应检入SCM,然后将密钥设置在清单占位符中。通过将密钥放入资源文件中,您会向任何访问您的存储库的人公开它们。 - JTT
5
这应该是被接受的答案。为了区分变体,您可以执行以下操作:android.applicationVariants.all { variant -> def mergedFlavor = variant.getMergedFlavor() if (variant.buildType.name.equals("debug") && variant.flavorName.equals("demo")) { mergedFlavor.manifestPlaceholders = [appPackageId: "myPackage1"] } } - Aleksander Niedziolko
在使用“connected”测试时遇到了问题,其他情况下都可以正常工作。 - StuStirling

14

我猜你是指Fabric ApiKey?:) 我刚刚花了几个小时试图通过占位符和在gradle文件中指定ApiKey以类似的方式来完成它,尽管自com.android.tools.build:gradle:1.3.1以来似乎不可能。可以为特定口味指定占位符,但不能为口味和构建类型同时指定。

只是为了纠正语法,如果可能的话,你需要这样做(如果可能的话),但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>
< p > manifestMerger将处理替换,因此您最终将在每种情况下都获得正确的密钥。我刚刚成功地实施了它。我只是希望您确实是在引用Fabric密钥!:) < p > 希望这能帮到您!

谢谢您的帖子!由于我的应用程序结构有些独特,我实际上找到了另一种方法来完成它,但我确实认为您的方法可能是正确的。我熟悉Fabric,但不幸的是这是用于集成Localytics(一个相当不错的分析平台)。再次感谢您的帖子! :) - Stoph
@Stoph,你介意分享一下你是怎么做到的吗?我也在寻找一种处理Localytics的方法。 - Nelson Ramirez
@NelsonRamirez - 我实际上将此任务交给了另一个开发人员,现在看代码,似乎他们的成功并不比我更多。看起来他们只是使用调试和发布密钥,并在调试/发布构建类型中设置manifestPlaceholder。很抱歉我没有更好的答案。 - Stoph
4
您可以使用variant.mergedFlavor.manifestPlaceholders =更新值,而无需像@Morten Holmgaard回答中建议的那样为每个变体创建一个AndroidManifest.xml - jkasten
无法为类型为com.android.build.gradle.internal.api.ApplicationVariantImpl的对象设置未知属性'manifestPlaceholders'。 - Rissmon Suresh
请查看以下链接以获取有关如何在Python中使用OpenCV进行图像处理的详细信息:https://dev59.com/cFwZ5IYBdhLWcg3wbv7r#65657119 - Mudassar Ashraf

10

我在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"
    }
}

7
与被接受的答案类似,如果您不想重复清单文件,可以使用字符串资源来完成此操作。
例如,如果您有两个变体(flavor1和flavor2),则将得到以下源集。
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>

在每个口味 / 构建的 sourceSets 中,只需引用这些键。例如,flavor1Release/res/values/strings.xml。
<resources>
     <string name="apiKey">@string/flavor1ReleaseKey</string>
</resources>

1
这看起来像是对我们有用的东西。谢谢你的新答案! - Stoph
这个没有起作用,因为它抛出了 Crashlytics 开发者工具错误 - Cerlin
请查看以下链接,无需重复回答: https://dev59.com/cFwZ5IYBdhLWcg3wbv7r#65657119 - Mudassar Ashraf

1
我相信你需要一个manifestPlaceHolder来在Java代码中读取该值,是吗?如果是这样,你已经可以在生成的BuildConfig.java中读取FLAVOR名称。例如,如果你定义了一个名为smartphone的flavor,你可以使用BuildConfig.FLAVOR字符串访问该值;然后在你的代码中,你可以使用一个简单的if (BuildConfig.FLAVOR.equals("smartphone"))... 但是,也许你需要读取应用程序的某种配置,如apiKey。在这种情况下,最好的方法是为每个flavor创建一个类或字符串资源;这是link给你的建议。

这不完全是我想做的,但是关于能够访问 BuildConfig.java 中的内容的说明很有帮助。我可能可以利用它来完成我需要的事情。谢谢! - Stoph
那么,你能解释一下你想要做什么吗? - Mimmo Grottoli
我的理解是,Gradle将为每个Build Type / Product Flavor组合创建一个Build Variant。我只想找到一种方法,在productFlavor块中检查Built Type,以便确定在我的manifestPlaceholder中使用哪个硬编码字符串API密钥。如果还不清楚,请告诉我。谢谢! - Stoph
1
好的。不要在脚本中硬编码您的API密钥,而是可以将其硬编码到代码中。您可以为每个所需的风味或变体定义一个apiKey。阅读此指南 - 为每个风味添加其他源目录部分。该示例讨论了不同风味的不同活动,但您可以应用相同的示例以便拥有不同的资源或不同的类甚至不同的清单文件。希望能够帮助到您。 - Mimmo Grottoli
我知道我们已经在为其他文件做类似的事情,所以我可能可以借此来完成我需要的东西。谢谢你的帮助! - Stoph
我还没有机会实现你的建议,所以我不知道它是否正确。尽管如此,我仍然相信它可能有效,如果你编辑你的主要回复来包含或引用你后来的评论,那么我会接受它。谢谢! - Stoph

1
您不需要重复的文件
Build.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}" />

1
我所做的是将当前的AndroidManifest.xml复制到app/src/debug中,并在那里更改了调试清单中的密钥:
 <meta-data
            android:name="com.crashlytics.ApiKey"
            tools:replace="android:value"
            android:value="@string/crashlytics_debug" />

app/src/main清单文件如下:

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

0
作为对 @Eric 帖子的补充,对于 AGP 版本 com.android.tools.build:gradle:4.x,此代码片段
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"
    }
}

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