Ok, so here’s a bit more Gradle hacking.

Note that the following workaround becomes obsolete once the guys at Google add support for placeholders in resource files.

So the problem is as follows:

  1. You have a SyncAdapter.

  2. You have multiple build variants.

WAAAAT? Yes, that’s already enough to cause some headaches, assuming you wanna install multiple build variants side by side.




So before we’re getting right into the root of the problem, note that this article might as well take one of the following titles:

  • How to provide unique authorities per build variant for your SyncAdapter, AccountAuthenticator and/or ContentProvider
  • How to enable the ${applicationId} placeholder in <sync-adapter />, <searchable /> and/or <account-authenticator /> XML files
  • How to avoid the dreaded INSTALL_FAILED_CONFLICTING_PROVIDER exception

If you wonder what the exception in the last title is all about, it occurs if two apps (e.g. debug and release) are registering for the same ContentProvider.




So why is this a problem at all? Well, the authority of a SyncAdapter as listed in android:authorities must be unique. As per the official Android documentation:

A provider usually has a single authority, which serves as its Android-internal name. To avoid conflicts with other providers, you should use Internet domain ownership (in reverse) as the basis of your provider authority.

Now, for ContentProviders that are specified in your AndroidManifest.xml file this is not a problem at all. You can just use the {applicationId} placeholder as follows:

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

Unfortunately, {applicationId} does not work in other XML resource files.

So how are we gonna fix this? Gradle to the rescue! What we’re gonna do is dynamically generate a new XML String resource file that provides unique authorities for the current build variant.

To do that, firstly, we need to tell our build script that the task that merges the resources (called mergeResources) depends on our own task which I’ve called createBuildVariantResources:

android {
	applicationVariants.all { variant ->
    	 variant.mergeResources.dependsOn {
            createBuildVariantResources(variant)
        }
    }
}

That task takes the build variant as a parameter. You will soon see why.

But first, let’s create a simple helper method that returns the application ID for a given build variant:

/**
 * @return the applicationId for the given build variant
 */
ext.applicationId = { variant ->
    def applicationId = variant.productFlavors[0].applicationId
    def suffix = variant.buildType.applicationIdSuffix
    applicationId += suffix ?: ""
}

I think this snippet doesn’t need much explanation. It grabs both the app ID and a potential app suffix from the given build variant and returns them as a concatenated String. You can put this anywhere in your build.gradle file. I usually place it outside of the Android closure at the bottom of the build script.

Now, let’s generate the resources. Note that regular Strings in Groovy cannot span multiple lines. So we’re gonna use triple double quotes to create a multi-line GString that supports interpolating variables:

/**
 * Generates unique resources for the given build variant that are used
 * by our sync adapter, account authenticator, content provider, etc.
 * The resources are based on the application id so that each build variant can be installed
 * alongside each other without causing the dreaded "INSTALL_FAILED_CONFLICTING_PROVIDER" exception.
 */
ext.createBuildVariantResources = { variant ->
    def applicationId = applicationId(variant)
    def syncAuthority = applicationId + ".sync"
    def syncAccountType = applicationId + ".provider"
    def res = """<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Automatically generated file. DO NOT MODIFY. -->

    <string name="application_id">${applicationId}</string>
    <string name="sync_authority">${syncAuthority}</string>
    <string name="sync_account_type">${syncAccountType}</string>

</resources>"""
	def folder = new File("${buildDir}/generated/res/generated/${variant.dirName}/values")
	folder.mkdirs()
	def file = new File(folder, "variant_resources.xml")
	file.createNewFile()
	file.write(res)
}

I think the code itself is again self-explanatory. It creates a new file called variant_resources.xml and stores it in the project’s generated values folder. I also added the application ID which you might as well obtain via BuildConfig.APPLICATION_ID.

And that’s it. You can now reference your SyncAdapter’s authority using @string/sync_authority which is guaranteed to be unique per build variant since it’s based on the current variant’s application ID.




One last note: resource-based authorities only work on Android 2.2.1 and later. This shouldn’t be a problem for you these days. minSdkVersion=15 FTW!

Also: Android Studio doesn’t yet recognize resources that are generated by Gradle. So you will see that the references from above are being marked in red and annotated with Cannot resolve symbol '@string/sync_authority'. However, this is only Android Studio moaning. You can safely ignore that. It will be fixed soon.




So this solution is obviously not elegant at all, but it does the job. For now. Consider it a temporary workaround until another update of the Android Gradle plugin will be released that fixes this problem.