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

Using a different manifestPlaceholder for each Build Variant

首先我要说我是 Gradle 的新手,所以如果有人回答了这个问题,我深表歉意。

我正在开发一个 Android 应用程序,该应用程序使用 API 密钥访问第 3 方工具。根据应用程序的 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 语句在一个非常简单的情况下工作,但我不知道如何从 中引用 buildType ]productFlavors 块,以便我可以将其用作条件。

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

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

我猜你指的是 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>

manifestMerger 将处理替换,您将在每个场景中得到正确的密钥。我刚刚成功实施了它。我只是希望你真的指的是 Fabric 键! :)

希望对您有所帮助!

与接受的答案类似,如果您不想复制您的清单,您可以使用字符串资源来完成。

例如,如果您有两种口味(口味 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 中定义它们。然后让 flavor/build 源集引用那些。

例如:

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

然后在每个 flavor/build sourceSets 中,您只需引用这些键。

flavor1Release/res/values/strings.xml

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

您可以通过访问特定 applicationVariant 的 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 = ...
}

我所做的是将当前 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" />

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

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

作为@Eric post 的补充,对于 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"
    }
}