在Gradle中使用构buildtypes来运行在一台设备上使用ContentProvider的相同应用程序

我已经设置Gradle添加包名称后缀到我的debugging应用程序,所以我可以有我使用和debugging版本在一个手机上的发行版本。 我引用这个: http : //tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types

我的build.gradle文件如下所示:

... android { ... buildTypes { debug { packageNameSuffix ".debug" versionNameSuffix " debug" } } } 

一切工作正常,直到我开始在我的应用程序中使用ContentProvider。 我得到:

 Failure [INSTALL_FAILED_CONFLICTING_PROVIDER] 

我知道这是因为两个应用程序(发行版和debugging版)注册了相同的ContentProvider权限。

我看到一个可能性来解决这个问题。 如果我理解正确,你应该能够指定不同的文件来build设时使用。 然后,我应该能够将不同的权限放在不同的资源文件中(并且从Manifest设置权限作为string资源),并告诉Gradle使用不同的资源进行debugging构build。 那可能吗? 如果是,那么关于如何实现这一点的提示将是非常棒的!

或者也许有可能使用Gradle直接修改Manifest? 任何其他解决scheme,如何在一台设备上运行与ContentProvider相同的应用程序总是受欢迎的。

现在的答案都没有让我满意,但是自由已经接近了。 所以我就是这么做的 首先,我正在与之合作:

  • Android Studio Beta 0.8.2
  • Gradle插件0.12。+
  • Gradle 1.12

我的目标是使用同一个ContentProvider在同一个设备上运行Debug版本和Release版本。


在你的应用程序的build.gradle后缀的debugging版本:

 buildTypes { debug { applicationIdSuffix ".debug" } } 

AndroidManifest.xml文件中设置你的ContentProvider android:authorities属性:

 <provider android:name="com.example.app.YourProvider" android:authorities="${applicationId}.provider" android:enabled="true" android:exported="false" > </provider> 

在您的代码AUTHORITY属性,可以在您的实现需要的地方使用:

 public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider"; 

提示:之前是BuildConfig.PACKAGE_NAME

而已! 它会像魅力一样工作。 如果您使用SyncAdapter,请继续阅读!


SyncAdapter更新(14.11.2014)

我再次从我目前的设置开始:

  • Android Studio Beta 0.9.2
  • Gradle插件0.14.1
  • Gradle 2.1

基本上,如果你需要为不同的构build定制一些值,你可以从build.gradle文件中完成:

  • 使用buildConfigFieldBuildConfig.java类访问它
  • 使用resValue从资源,例如@ string / your_value来访问它

作为资源的替代方法,您可以创build单独的buildType或flavor目录,并覆盖其中的XML或值。 但是,我不打算在下面的例子中使用它。


build.gradle文件中添加以下内容:

 defaultConfig { resValue "string", "your_authorities", applicationId + '.provider' resValue "string", "account_type", "your.syncadapter.type" buildConfigField "String", "ACCOUNT_TYPE", '"your.syncadapter.type"' } buildTypes { debug { applicationIdSuffix ".debug" resValue "string", "your_authorities", defaultConfig.applicationId + '.debug.provider' resValue "string", "account_type", "your.syncadapter.type.debug" buildConfigField "String", "ACCOUNT_TYPE", '"your.syncadapter.type.debug"' } } 

您将在BuildConfig.java类中看到结果

 public static final String ACCOUNT_TYPE = "your.syncadapter.type.debug"; 

并在build / generated / res / generated / debug / values / generated.xml中

 <?xml version="1.0" encoding="utf-8"?> <resources> <!-- Automatically generated file. DO NOT MODIFY --> <!-- Values from default config. --> <item name="account_type" type="string">your.syncadapter.type.debug</item> <item name="authorities" type="string">com.example.app.provider</item> </resources> 

在您的authenticator.xml中使用build.gradle文件中指定的资源

 <?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="@string/account_type" android:icon="@drawable/ic_launcher" android:smallIcon="@drawable/ic_launcher" android:label="@string/app_name" /> 

在你的syncadapter.xml文件中再次使用相同的资源,也使用@ string / authority

 <?xml version="1.0" encoding="utf-8"?> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="@string/authorities" android:accountType="@string/account_type" android:userVisible="true" android:supportsUploading="false" android:allowParallelSyncs="false" android:isAlwaysSyncable="true" /> 

提示:自动完成(Ctrl +空格)不适用于这些生成的资源,因此您必须手动input它们

新的Android构build系统提示:ContentProvider权限重命名

我猜你们都听说过新的基于Android Gradle的构build系统。 说实话,这个新的构build系统比前一个系统向前迈进了一大步。 这还不是最终的(截至撰写本文时为止,最新的版本是0.4.2),但是您已经可以在大多数项目中安全地使用它。

我已经把我的大部分项目转换到这个新的构build系统,并且由于在某些特定情况下缺乏支持而出现了一些问题。 其中之一是对ContentProvider权限重命名的支持

新的Android构build系统可以让您在构build时只需修改软件包名称即可处理不同types的应用程序。 这种改进的主要优势之一是,您现在可以同时在同一设备上安装两个不同版本的应用程序。 例如:

 android { compileSdkVersion 17 buildToolsVersion "17.0.0" defaultConfig { packageName "com.cyrilmottier.android.app" versionCode 1 versionName "1" minSdkVersion 14 // Listen to +Jeff Gilfelt advices :) targetSdkVersion 17 } buildTypes { debug { packageNameSuffix ".debug" versionNameSuffix "-debug" } } } 

使用这样的Gradleconfiguration,你可以组装两个不同的APK:

•包含com.cyrilmottier.android.app.debug包名的debuggingAPK•包含com.cyrilmottier.android.app包的发行版APK

唯一的问题是你将无法同时安装两个APK,如果他们都暴露一个ContentProvider与相同的权限。 从逻辑上来说,我们需要根据当前的构buildtypes来重新命名权限……但是Gradle构build系统不支持这个function(但是我相信它很快就会被修复)。 所以这里有一条路要走:

首先,我们需要将提供者Android清单ContentProvider声明移动到适当的构buildtypes。 为了做到这一点,我们只需要:

SRC /debugging/ AndroidManifest.xml中

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cyrilmottier.android.app" android:versionCode="1" android:versionName="1"> <application> <provider android:name=".provider.Provider1" android:authorities="com.cyrilmottier.android.app.debug.provider" android:exported="false" /> </application> </manifest> 

SRC /发行/ AndroidManifest.xml中

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cyrilmottier.android.app" android:versionCode="1" android:versionName="1"> <application> <provider android:name=".provider.Provider1" android:authorities="com.cyrilmottier.android.app.provider" android:exported="false" /> </application> </manifest> 

请确保从src / main /中的AndroidManifest.xml中删除ContentProvider声明,因为Gradle不知道如何合并具有相同名称但具有不同权限的ContentProvider。

最后,我们可能需要访问代码中的权限。 这可以使用BuildConfig文件和buildConfig方法很容易地完成:

 android { // ... final PROVIDER_DEBUG = "com.cyrilmottier.android.app.debug.provider" final PROVIDER_RELEASE = "com.cyrilmottier.android.app.provider" buildTypes { debug { // ... buildConfigField "String", "PROVIDER_AUTHORITY", PROVIDER_DEBUG } release { buildConfigField "String", "PROVIDER_AUTHORITY", PROVIDER_RELEASE } } } 

感谢这个解决方法,您可以在您的ProviderContract中使用BuildConfig.PROVIDER_AUTHORITY,并同时安装两个不同版本的应用程序。


在Google+上的原创: https : //plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ

虽然Cyril的例子工作得很好,如果你只有一些构buildtypes,如果你有很多不同的构buildtypes和/或产品风格,它会很快变得复杂,因为你需要维护许多不同的AndroidManifest.xml。

我们的项目由3种不同的构buildtypes和6种不同的构build方式组成,所以我们增加了对ContentProvider权限中的“.res-auto”的支持,扩展到当前的包名,并且不需要维护不同的AndroidManifest.xml

 /** * Version 1.1. * * Add support for installing multiple variants of the same app which have a * content provider. Do this by overriding occurrences of ".res-auto" in * android:authorities with the current package name (which should be unique) * * V1.0 : Initial version * V1.1 : Support for ".res-auto" in strings added, * eg. use "<string name="auth">.res-auto.path.to.provider</string>" * */ def overrideProviderAuthority(buildVariant) { def flavor = buildVariant.productFlavors.get(0).name def buildType = buildVariant.buildType.name def pathToManifest = "${buildDir}/manifests/${flavor}/${buildType}/AndroidManifest.xml" def ns = new groovy.xml.Namespace("http://schemas.android.com/apk/res/android", "android") def xml = new XmlParser().parse(pathToManifest) def variantPackageName = xml.@package // Update all content providers xml.application.provider.each { provider -> def newAuthorities = provider.attribute(ns.authorities).replaceAll('.res-auto', variantPackageName) provider.attributes().put(ns.authorities, newAuthorities) } // Save modified AndroidManifest back into build dir saveXML(pathToManifest, xml) // Also make sure that all strings with ".res-auto" are expanded automagically def pathToValues = "${buildDir}/res/all/${flavor}/${buildType}/values/values.xml" xml = new XmlParser().parse(pathToValues) xml.findAll{it.name() == 'string'}.each{item -> if (!item.value().isEmpty() && item.value()[0].startsWith(".res-auto")) { item.value()[0] = item.value()[0].replace(".res-auto", variantPackageName) } } saveXML(pathToValues, xml) } def saveXML(pathToFile, xml) { def writer = new FileWriter(pathToFile) def printer = new XmlNodePrinter(new PrintWriter(writer)) printer.preserveWhitespace = true printer.print(xml) } // Post processing of AndroidManifest.xml for supporting provider authorities // across build variants. android.applicationVariants.all { variant -> variant.processManifest.doLast { overrideProviderAuthority(variant) } } 

示例代码可以在这里find: https : //gist.github.com/cmelchior/6988275

由于插件的版本是0.8.3(实际上是0.8.1,但工作不正常),您可以在构build文件中定义资源,因此这可能是一个更清洁的解决scheme,因为您不需要创buildstring文件,也不需要额外的debugging/发布文件夹。

的build.gradle

 android { buildTypes { debug{ resValue "string", "authority", "com.yourpackage.debug.provider" } release { resValue "string", "authority", "com.yourpackage.provider" } } } 

AndroidManifest.xml中

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.yourpackage" android:versionCode="1" android:versionName="1"> <application> <provider android:name=".provider.Provider1" android:authorities="@string/authority" android:exported="false" /> </application> </manifest> 

我不知道有没有人提到它。 实际上,在Android的gradle插件0.10+之后,清单合并将提供官方对这个函数的支持: http ://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger

在AndroidManifest.xml中,可以像这样使用$ {packageName}:

 <provider android:name=".provider.DatabasesProvider" android:authorities="${packageName}.databasesprovider" android:exported="true" android:multiprocess="true" /> 

而在你的build.gradle你可以有:

 productFlavors { free { packageName "org.pkg1" } pro { packageName "org.pkg2" } } 

请参阅完整示例: https : //code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152

并在这里: https : //code.google.com/p/anymemo/source/browse/build.gradle#41

在代码中使用xml中的${applicationId}占位符和BuildConfig.APPLICATION_ID

您将需要扩展构build脚本以在清单以外的xml文件中启用占位符。 您可以使用每个构build版本的源代码目录来提供不同版本的xml文件,但维护将很快变得麻烦。

AndroidManifest.xml中

您可以使用清单中开箱即用的applicationId占位符。 声明你的提供者是这样的:

 <provider android:name=".provider.DatabaseProvider" android:authorities="${applicationId}.DatabaseProvider" android:exported="false" /> 

请注意${applicationId}位。 在构build时,这将replace为正在构build的构build变体的实际applicationId。

在代码中

您的ContentProvider需要在代码中构build授权string。 它可以使用BuildConfig类。

 public class DatabaseContract { /** The authority for the database provider */ public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".DatabaseProvider"; // ... } 

请注意BuildConfig.APPLICATION_ID位。 它是一个生成的类,具有正在构build的构build变体的实际applicationId。

res / xml /文件,例如syncadapter.xml,accountauthenticator.xml

如果要使用同步适配器,则需要在res / xml /目录中的xml文件中为ContentProvider和AccountManager提供元数据。 applicationId占位符在这里不受支持。 但是你可以自己扩展构build脚本来破解它。

 <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="${applicationId}" android:allowParallelSyncs="false" android:contentAuthority="${applicationId}.DatabaseProvider" android:isAlwaysSyncable="true" android:supportsUploading="true" android:userVisible="true" /> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="${applicationId}" android:icon="@drawable/ic_launcher" android:label="@string/account_authenticator_label" android:smallIcon="@drawable/ic_launcher" /> 

再次注意${applicationId} 。 这只适用于将下面的gradle脚本添加到模块的根目录并从build.gradle应用它。

的build.gradle

从模块build.gradle脚本中应用额外的构build脚本。 Android gradle插件下方是个好地方。

 apply plugin: 'com.android.application' apply from: './build-processApplicationId.gradle' android { compileSdkVersion 21 // etc. 

集结processApplicationId.gradle

以下是res / xml / placeholder构build脚本的源代码。 一个更好的文档版本可以在github上find 。 欢迎改进和扩展。

 def replace(File file, String target, String replacement) { def result = false; def reader = new FileReader(file) def lines = reader.readLines() reader.close() def writer = new FileWriter(file) lines.each { line -> String replacedLine = line.replace(target, replacement) writer.write(replacedLine) writer.write("\n") result = result || !replacedLine.equals(line) } writer.close() return result } def processXmlFile(File file, String applicationId) { if (replace(file, "\${applicationId}", applicationId)) { logger.info("Processed \${applicationId} in $file") } } def processXmlDir(File dir, String applicationId) { dir.list().each { entry -> File file = new File(dir, entry) if (file.isFile()) { processXmlFile(file, applicationId) } } } android.applicationVariants.all { variant -> variant.mergeResources.doLast { def applicationId = variant.mergedFlavor.applicationId + (variant.buildType.applicationIdSuffix == null ? "" : variant.buildType.applicationIdSuffix) def path = "${buildDir}/intermediates/res/${variant.dirName}/xml/" processXmlDir(new File(path), applicationId) } } 

strings.xml中

在我看来,没有必要为资源string添加占位符支持。 对于上述用例,至less不需要。 但是,您可以轻松地将脚本更改为不仅replaceres / xml /目录中的占位符,而且还将其replace为res / values /目录中的占位符。

我宁愿喜欢西里尔和rciovati之间的混合物。 我觉得更简单,你只有两个修改。

build.gradle看起来像:

 android { ... productFlavors { production { packageName "package.name.production" resValue "string", "authority", "package.name.production.provider" buildConfigField "String", "AUTHORITY", "package.name.production.provider" } testing { packageName "package.name.debug" resValue "string", "authority", "package.name.debug.provider" buildConfigField "String", "AUTHORITY", "package.name.debug.provider" } } ... } 

AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="package.name" > <application ...> <provider android:name=".contentprovider.Provider" android:authorities="@string/authority" /> </application> </manifest> 

基于@ChristianMelchior的样本,这是我的解决scheme,它解决了以前解决scheme中的两个问题:

  • 在build目录中更改values.xml的解决scheme会导致资源的完全重build(包括所有drawable的适配)

  • 由于不明原因,IntelliJ(也可能是Android Studio)不能可靠地处理资源,导致构build包含未replace的.res-auto提供程序权限

这个新的解决scheme通过创build一个新的任务来执行更多的Gradle方法,并通过定义input和输出文件来允许增量构build。

  1. 创build一个文件(在我把它放在一个variants目录中),格式化为一个资源XML文件,其中包含string资源。 这些将被合并到应用程序的资源中,值中的任何.res-auto将被replace为变体的包名称,例如<string name="search_provider">.res-auto.MySearchProvider</string>

  2. 从这个要点添加build_extras.gradle文件到您的项目,并从主build.gradle通过添加apply from: './build_extras.gradle'android块之上的某个地方

  3. 请确保通过将其添加到build.gradleandroid.defaultConfig块来设置默认包名称

  4. AndroidManifest.xml和其他configuration文件(例如自动完成search提供程序的xml/searchable.xml )中,引用提供者(例如@string/search_provider

  5. 如果您需要获取相同的名称,则可以使用BuildConfig.PACKAGE_NAMEvariables,例如BuildConfig.PACKAGE_NAME + ".MySearchProvider"

https://gist.github.com/paour/9189462


更新:此方法仅适用于Android 2.2.1及更高版本。 对于早期的平台,看到这个答案 ,它有自己的问题,因为新的清单合并仍然是非常粗糙的边缘…

gradle.build

 android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.example.awsomeapp" minSdkVersion 9 targetSdkVersion 23 versionCode 1 versionName "1.0.0" } productFlavors { prod { applicationId = "com.example.awsomeapp" } demo { applicationId = "com.example.awsomeapp.demo" versionName = defaultConfig.versionName + ".DEMO" } } buildTypes { release { signingConfig signingConfigs.release debuggable false minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } debug { applicationIdSuffix ".debug" versionNameSuffix = ".DEBUG" debuggable true } } applicationVariants.all { variant -> variant.outputs.each { output -> // rename the apk def file = output.outputFile; def newName; newName = file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"); newName = newName.replace(project.name, "awsomeapp"); output.outputFile = new File(file.parent, newName); } //Generate values Content Authority and Account Type used in Sync Adapter, Content Provider, Authenticator def valueAccountType = applicationId + '.account' def valueContentAuthority = applicationId + '.authority' //generate fields in Resource string file generated.xml resValue "string", "content_authority", valueContentAuthority resValue "string", "account_type", valueAccountType //generate fields in BuildConfig class buildConfigField "String", "ACCOUNT_TYPE", '"'+valueAccountType+'"' buildConfigField "String", "CONTENT_AUTHORITY", '"'+valueContentAuthority+'"' //replace field ${valueContentAuthority} in AndroidManifest.xml mergedFlavor.manifestPlaceholders = [ valueContentAuthority: valueContentAuthority ] } } 

authenticator.xml

 <?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="@string/account_type" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:smallIcon="@drawable/ic_launcher" /> 

sync_adapter.xml

 <?xml version="1.0" encoding="utf-8"?> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="@string/content_authority" android:accountType="@string/account_type" android:userVisible="true" android:allowParallelSyncs="false" android:isAlwaysSyncable="true" android:supportsUploading="true"/> 

AndroidManifest.xml中

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0.0" package="com.example.awsomeapp"> <uses-permission android:name="android.permission.GET_ACCOUNTS"/><!-- SyncAdapter and GCM requires a Google account. --> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> <uses-permission android:name="android.permission.USE_CREDENTIALS"/> <!-- GCM Creates a custom permission so only this app can receive its messages. --> <permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/> <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/> <application.... ....... <!-- Stub Authenticator --> <service android:name="com.example.awsomeapp.service.authenticator.CAuthenticatorService" android:exported="true"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator"/> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator"/> </service> <!-- --> <!-- Sync Adapter --> <service android:name="com.example.awsomeapp.service.sync.CSyncService" android:exported="true" android:process=":sync"> <intent-filter> <action android:name="android.content.SyncAdapter"/> </intent-filter> <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/sync_adapter" /> </service> <!-- --> <!-- Content Provider --> <provider android:authorities="${valueContentAuthority}" android:exported="false" android:name="com.example.awsomeapp.database.contentprovider.CProvider"> </provider> <!-- --> </application> </manifest> 

码:

 public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY; public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE; 

我已经写了一个Github示例项目的博客post,以与Cyril稍有不同的方式处理这个问题(和其他类似的问题)。

http://brad-android.blogspot.com/2013/08/android-gradle-building-unique-build.html

不幸的是,Android插件的当前版本(0.4.1)似乎没有提供一个很好的解决scheme。 我还没有时间来尝试这个,但是对于这个问题可能的解决方法是使用stringresource @string/provider_authority ,并在manifest: android:authority="@string/provider_authority" 。 然后在每个构buildtypes的res文件夹中都有一个res/values/provider.xml文件,这个文件夹应该覆盖权限,在你的情况下,这将是src/debug/res

我已经研究过生成xml文件,但是在当前版本的插件中似乎没有任何好的钩子。 尽pipe如此,我build议提出一个function请求,我可以想象更多的人会遇到同样的问题。

这篇文章的答案适用于我。

http://www.kevinrschultz.com/blog/2014/03/23/using-android-content-providers-with-multiple-package-names/

我使用3种不同的口味,所以我创build了3个清单与内容提供商在每种口味kevinrschultz说:

 productFlavors { free { packageName "your.package.name.free" } paid { packageName "your.package.name.paid" } other { packageName "your.package.name.other" } } 

您的主要清单不包括提供者:

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" > <!-- Permissions --> <application> <!-- Nothing about Content Providers at all --> <!-- Activities --> ... <!-- Services --> ... </application> 

而你的清单在你的每一个风味,包括提供者。

自由:

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" > <application> <!-- Content Providers --> <provider android:name="your.package.name.Provider" android:authorities="your.package.name.free" android:exported="false" > </provider> </application> </manifest> 

支付:

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" > <application> <!-- Content Providers --> <provider android:name="your.package.name.Provider" android:authorities="your.package.name.paid" android:exported="false" > </provider> </application> </manifest> 

其他:

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" > <application> <!-- Content Providers --> <provider android:name="your.package.name.Provider" android:authorities="your.package.name.other" android:exported="false" > </provider> </application> </manifest> 

为什么不添加这个?

type.packageNameSuffix =“。$ type.name”

我的解决scheme是在AndroidManifest.xml使用占位符replace。 它还处理packageNameSuffix属性,因此您可以在同一设备上进行debugrelease以及任何其他自定义构build。

 applicationVariants.all { variant -> def flavor = variant.productFlavors.get(0) def buildType = variant.buildType variant.processManifest.doLast { println '################# Adding Package Names to Manifest #######################' replaceInManifest(variant, 'PACKAGE_NAME', [flavor.packageName, buildType.packageNameSuffix].findAll().join()) // ignores null } } def replaceInManifest(variant, fromString, toString) { def flavor = variant.productFlavors.get(0) def buildtype = variant.buildType def manifestFile = "$buildDir/manifests/${flavor.name}/${buildtype.name}/AndroidManifest.xml" def updatedContent = new File(manifestFile).getText('UTF-8').replaceAll(fromString, toString) new File(manifestFile).write(updatedContent, 'UTF-8') } 

如果你想看看它是否会在以后发展的话,我也会把它写出来。

我发现这是比多种资源和XMLparsing方法更优雅的方法。