在 Gradle 使用构建类型在一个设备上运行使用 ContentProvider 的同一个应用程序

我已经设置了 Gradle,以添加软件包名称后缀到我的调试应用程序,这样我就可以有发布版本,我正在使用和调试版本的一个手机上。我指的是 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]

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

我看到了解决这个问题的一种可能性。如果我理解正确,您应该能够指定在构建时使用的不同文件。然后,我应该能够把不同的权限在不同的资源文件(并从清单设置权限作为字符串资源) ,告诉 Gradle 使用不同的资源进行调试构建。这可能吗?如果是的话,那么任何关于如何实现这一目标的提示都是非常棒的!

或者,也许可以直接修改使用格拉德的清单?任何关于如何在一个设备上使用 ContentProvider 运行相同应用程序的其他解决方案都是受欢迎的。

56415 次浏览

不幸的是,当前版本(0.4.1)的 android 插件似乎没有提供一个很好的解决方案。我还没有时间尝试这个,但是解决这个问题的一个可能的方法是使用字符串资源 @string/provider_authority,并在清单中使用它: android:authority="@string/provider_authority"。然后,在每种构建类型的 res 文件夹中都有一个应该覆盖该权限的 res/values/provider.xml,在您的情况下,这将是 src/debug/res

我研究了动态生成 xml 文件,但是在当前版本的插件中似乎没有任何好的钩子。我建议提交一个特性请求,我可以想象更多的人会遇到同样的问题。

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

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

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

新的 Android 构建系统允许您在构建时通过简单地修改软件包名称来处理不同类型的应用程序。这种改进的一个主要优点是,你现在可以在同一个设备上同时安装两个不同版本的应用程序。例如:

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

使用这样一个 Gradle 配置,您可以组装两个不同的 APK:

•使用 com.cyrilmottier.android.app.debug 包名称的调试 APK •使用 com.cyrilmottier.android.app 软件包名称的发行版 APK

唯一的问题是,如果两个 APK 都公开了具有相同权限的 ContentProvider,那么您将无法同时安装这两个 APK。相当合乎逻辑的是,我们需要根据当前的构建类型来重命名权威... ... 但是这并不受 Gradle 构建系统的支持(还没有?)?... 我相信很快就会修好的)。因此,我们可以这样做:

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

Src/debug/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/release/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

我曾经用 Github 示例项目写过一篇博文,用一种与 Cyril 略有不同的方式来解决这个问题(和其他类似的问题)。

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

虽然 Cyril 的示例在只有几种构建类型的情况下工作得很好,但是如果需要维护大量不同的 AndroidManifest.xml 的构建类型和/或产品风格,那么它很快就会变得复杂。

我们的项目由3种不同的构建类型和6种风格组成,共有18种构建变体,因此我们增加了对“。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)
}
}

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

为什么不加上这个呢?

PackageNameAffix = “ . $type.name”

基于@ChristianMelchior 的示例,下面是我的解决方案,它解决了以前解决方案中的两个问题:

  • 修改构建目录中 values.xml 的解决方案会导致完全重新构建资源(包括所有可绘制文件的适配器)

  • 由于未知的原因,IntelliJ (可能还有 Android Studio)不能可靠地处理资源,导致构建包含未替换的 .res-auto提供程序权限

这个新的解决方案通过创建一个新的任务来实现更多的 Gradle 方式,并通过定义输入和输出文件来允许增量构建。

  1. 创建一个文件(在示例中,我将其放在 variants目录中) ,格式类似于包含字符串资源的 xml 文件。这些将被合并到应用程序的资源中,值中出现的任何 .res-auto将被替换为变体的包名称,例如 <string name="search_provider">.res-auto.MySearchProvider</string>

  2. build_extras.gradle文件从 这个要点添加到您的项目中,并通过将 apply from: './build_extras.gradle'添加到 android块之上的某个位置来从主 build.gradle引用它

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

  4. AndroidManifest.xml和其他配置文件(比如用于自动完成搜索提供程序的 xml/searchable.xml)中,引用提供程序(例如 @string/search_provider)

  5. 如果需要获得相同的名称,可以使用 BuildConfig.PACKAGE_NAME变量,例如 BuildConfig.PACKAGE_NAME + ".MySearchProvider"

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


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

由于插件版本0.8.3(实际上是0.8.1,但是它不能正常工作) ,你可以在构建文件中定义资源,这样可以是一个更干净的解决方案,因为你不需要创建字符串文件或者额外的调试/发布文件夹。

建造,分级

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>

我的解决方案是在 AndroidManifest.xml中使用占位符替换。它还处理 packageNameSuffix属性,因此您可以拥有 debugrelease以及同一设备上的任何其他自定义构建。

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')
}

如果你想知道它以后是否会进化,我也把它放在 gist上了。

我发现这是一种比多资源和 XML 解析方法更优雅的方法。

不知道有没有人提过。事实上,在 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" />

在你的建筑里,你可以拥有:

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

现有的答案没有一个能让我满意,然而自由离我很近。所以这就是我怎么做到的。 首先,目前我的工作对象是:

  • Android Studio Beta 0.8.2
  • Gradle 插件0.12. +
  • 1.12年级

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


建造,分级中,应用程序集的后缀为 Debug build:

buildTypes {
debug {
applicationIdSuffix ".debug"
}
}

AndroidManifest.xml文件中设置 ContentProviderandroid: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之前

就是这样! 它会像魔法一样工作。继续阅读,如果你使用同步适配器!


同步适配器更新(14.11.2014)

我将再次从我当前的设置开始:

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

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

  • 使用 BuildConfigFieldBuildConfig.java类访问它
  • 使用 重估价值从资源访问它,例如 @ string/your _ value

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

例子


建造,分级文件中添加以下内容:

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>

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"
/>

在您的 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 + Space)不适用于这些生成的资源,因此必须手动输入它们

我更喜欢西里尔和瑞乔瓦蒂的混合物。我认为更简单,你只有两个修改。

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>

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

您将需要扩展构建脚本,以启用除清单之外的 xml 文件中的占位符。您可以使用每个构建变体的源目录来提供 xml 文件的不同版本,但是维护将变得非常麻烦。

AndroidManifest.xml

可以在清单中立即使用 applicationId 占位符:

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

注意 ${applicationId}位。这在构建时被替换为正在构建的构建变量的实际 applicationId。

用密码

ContentProvider 需要在代码中构造权限字符串。

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

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

Res/xml/files,例如 syncAdapter.xml,Accountauthenticator.xml

如果希望使用 Sync Adapter,则需要在 res/xml/目录中的 xml 文件中为 ContentProvider 和 AccountManager 提供元数据。这里不支持 applicationId 占位符。但是您可以自己扩展构建脚本来侵入它。

<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 脚本中的额外构建脚本。

apply plugin: 'com.android.application'
apply from: './build-processApplicationId.gradle'


android {
compileSdkVersion 21
// etc.

Build-processApplicationId. gradle

下面是 res/xml/占位符构建脚本的工作源代码。Github提供了一个更好的文档版本。欢迎改进和扩展。

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)
}
}

Xml

在我看来,没有必要为资源字符串添加占位符支持。对于上面的用例,至少它是不需要的。然而,您可以轻松地更改脚本,不仅替换 res/xml/目录中的占位符,而且替换 res/values/目录中的占位符。

这篇文章的答案对我很有用。

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

我使用3种不同的口味,所以我创建了3个清单与内容提供商在每种口味凯文舒尔茨说:

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>

格拉德,建造

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 ]
}
}

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;