Utiliser les types de construction dans Gradle pour exécuter la même application qui utilise ContentProvider sur un périphérique

j'ai mis en place Gradle pour ajouter le suffixe du nom du paquet à mon application de débogage afin que je puisse avoir la version de publication que j'utilise et la version de débogage sur un téléphone. Je faisais référence à ceci: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types

Mon build.le fichier gradle ressemble à ceci:

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

Tout fonctionne bien jusqu'à ce que je commence à utiliser un ContentProvider dans mon application. Je comprends:

Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]

je comprends que cela arrive parce que deux applications (release et debug) enregistrent la même autorité ContentProvider.

je vois une solution. Si je comprends correctement, vous devriez être en mesure de spécifier différents fichiers à utiliser lors de la construction. Alors je devrais être capable de mettre différentes autorités dans différents fichiers de ressources (et de manifester l'autorité comme ressource de chaîne) et de dire à Gradle d'utiliser différentes ressources pour debug build. Être - ce possible? Si oui, alors tous les conseils sur la manière d'y parvenir serait génial!

ou peut-être est-il possible de modifier directement le manifeste en utilisant Gradle? Toute autre solution sur la façon d'exécuter la même application avec ContentProvider sur un appareil est toujours la bienvenue.

115
demandé sur Jonik 2013-05-27 20:58:33

14 réponses

aucune des réponses existantes ne me satisfaisait, cependant la liberté était proche. Donc, c'est comment suis-je en train de faire. Tout d'abord, en ce moment, je travaille avec:

  • Android Studio Beta 0.8.2
  • plugin Gradle de 0,12.+
  • Grad 1.12

Mon objectif est d'exécuter de 151980920" version Release version sur le même appareil à l'aide de la même ContentProvider .


Dans "1519270920 de construire".Grad de votre suffixe app set Pour Debug build:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Dans AndroidManifest.xml fichiers jeu android:authorities propriété de votre ContentProvider :

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

dans votre code ensemble AUTHORITY biens qui peuvent être utilisés chaque fois que nécessaire dans votre mise en œuvre:

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

Conseil: avant BuildConfig.PACKAGE_NAME

C'est ça! Il fonctionne comme un charme. Continuez à lire si vous utilisez SyncAdapter!


mise à jour pour SyncAdapter (14.11.2014)

une fois de plus, je vais commencer avec ma configuration actuelle:

  • Android Studio Beta 0.9.2
  • Gradle plugin 0.14.1
  • Grad 2.1

fondamentalement, si vous avez besoin de personnaliser certaines valeurs pour différentes constructions, vous pouvez le faire à partir de la construction.gradle fichier:

  • utiliser buildConfigField pour y accéder à partir de la BuildConfig.java classe
  • utiliser resValue pour y accéder à partir de ressources par exemple @string/your_value

comme alternative pour les ressources, vous pouvez créer des répertoires buildType ou flavour distincts et remplacer XMLs ou valeurs à l'intérieur de ceux-ci. Cependant, je ne vais pas l'utiliser dans l'exemple ci-dessous.

exemple


Dans "1519270920 de construire".Grad ajouter le fichier suivant:

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

, Vous verrez les résultats dans BuildConfig.java classe

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

et dans construire/generated/res/generated/debug/valeurs/générée.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>

dans votre authentificateur.xml utilisation de la ressource spécifiée dans la construction.dossier 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"
/>

dans votre "syncadapter 1519270920".xml utiliser le même ressources encore et @string/autorités trop

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

Conseil: autocompletion (Ctrl+Space) ne fonctionne pas pour ces ressources générées si vous devez les taper manuellement

208
répondu Damian Petla 2015-05-19 12:24:54

nouveau système de construction Android tip: ContentProvider autorité renommer

je suppose que vous avez tous entendu parler du nouveau système de construction basé sur Android Gradle. Soyons honnêtes, ce nouveau système de construction est un grand pas en avant par rapport à la précédente. Il n'est pas encore définitifs (comme d'écrire ces lignes, la dernière version 0.4.2) mais vous pouvez déjà l'utiliser en toute sécurité dans la plupart de vos projets.

j'ai personnellement changé la plupart de mes le projet a eu des problèmes en raison du manque de soutien dans certaines situations particulières. L'un d'entre eux est le soutien à ContentProvider autorité renommer

le nouveau système Android vous permet de gérer différents types de votre application en modifiant simplement le nom du paquet au moment de la construction. L'un des principaux avantage de cette amélioration est que vous pouvez maintenant avoir deux versions différentes de votre application installée sur le même appareil en même temps. Par exemple:

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

en utilisant une telle configuration Gradle, vous pouvez assembler deux APKs différents:

• Un APK de débogage avec le com.cyrilmottier.Android.App.paquet de débogage nom • Une version APK avec le com.cyrilmottier.Android.package de l'application nom de

le seul problème avec cela est que vous ne pourrez pas installer les deux Apk en même temps s'ils exposent tous les deux un ContentProvider avec les mêmes autorités. Assez logiquement nous devons renommer l'autorité en fonction du type de construction actuel ... mais cela n'est pas supporté par le système de construction Gradle (encore? ... Je suis sûr qu'il sera bientôt réparé). Donc voici une façon de procéder:

tout d'abord, nous devons déplacer le fournisseur de manifeste Android ContentProvider déclaration au type de construction approprié. Pour ce faire, nous aurons tout simplement:

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

assurez-vous de supprimer la déclaration ContentProvider de L'AndroidManifest.xml dans src/ main / parce que Gradle ne sait pas comment fusionner les fournisseurs de contenu ayant le même nom mais une autorité différente.

enfin, nous pourrions avoir besoin d'accéder à l'autorité du code. Cela peut être fait assez facilement en utilisant le fichier BuildConfig et la méthode 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
       }
   }
}

grâce à cette solution, vous pourrez utiliser BuildConfig.PROVIDER_AUTHORITY dans votre ProviderContract et installez deux versions différentes de votre application en même temps.


Originaly sur Google+: https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ

38
répondu Cyril Mottier 2014-07-25 09:23:57

tandis que L'exemple de Cyril fonctionne très bien si vous avez seulement quelques types de construction, il se complique rapidement si vous avez beaucoup de types de construction et/ou des saveurs de produit que vous avez besoin de maintenir beaucoup de différents AndroidManifest.xml.

notre projet se compose de 3 types de construction différents et 6 saveurs totalisant 18 variantes de construction, nous avons donc ajouté le support pour ".res-auto" dans les autorités ContentProvider, qui s'étendent au nom de paquet actuel et supprime la nécessité de maintenir différents 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)
    }
}

exemple de code peut être trouvé ici: https://gist.github.com/cmelchior/6988275

23
répondu Christian Melchior 2013-10-22 13:47:07

depuis la version 0.8.3 du plugin (en fait 0.8.1 mais cela ne fonctionnait pas correctement), vous pouvez définir des ressources dans le fichier de compilation, donc cela pourrait être une solution plus propre parce que vous n'avez pas besoin de créer des fichiers de chaînes de caractères ni des dossiers de débogage/publication supplémentaires.

"151950920 de construire".Grad

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>
20
répondu rciovati 2014-03-14 10:37:49

je ne sais pas si quelqu'un en parle. En fait, après Android gradle plugin 0.10+, la fusion manifeste fournira le support officiel de cette fonction: http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger

Dans AndroidManifest.xml, vous pouvez utiliser $ {packageName} comme ceci:

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

Et dans votre construction.gradle vous pouvez avoir:

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

voir plein exemple ici: https://code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152

et ici: https://code.google.com/p/anymemo/source/browse/build.gradle#41

13
répondu Liberty 2014-05-08 22:50:14

utiliser ${applicationId} les espaces réservés en xml et BuildConfig.APPLICATION_ID en code.

vous devrez étendre le script de compilation pour activer les espaces réservés dans les fichiers xml autres que le manifeste. Vous pouvez utiliser un répertoire source par variante de construction pour fournir différentes versions des fichiers xml, mais la maintenance deviendra lourde très rapidement.

AndroidManifest.xml

vous pouvez utiliser l'applicationId placeholder sorti de la boîte dans le manifeste. Déclarez votre fournisseur comme ceci:

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

noter le bit ${applicationId} . Ceci est remplacé au moment de la construction par l'applicationId réel pour la variante de construction qui est en cours de construction.

dans le code

votre ContentProvider doit construire la chaîne d'autorité en code. Il peut utiliser la classe BuildConfig.

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

Notez le BuildConfig.APPLICATION_ID . Il s'agit d'une classe générée avec l'applicationId réel pour la variante de construction en cours de construction.

res/xml/ files, p.ex. syncadapter.xml, accountauthenticator.xml

si vous souhaitez utiliser un adaptateur Sync, vous devez fournir des métadonnées pour le ContentProvider et le AccountManager dans les fichiers xml du répertoire res/xml/. Le placeholder applicationId n'est pas supporté ici. Mais vous pouvez étendre la construisez le script vous-même pour le pirater.

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

de nouveau, notez le ${applicationId} . Cela ne fonctionne que si vous ajoutez le script gradle ci-dessous à la racine de votre module et l'appliquez à partir de build.gradle.

"1519150920 de construire".Grad

appliquer le script de construction supplémentaire à partir de la construction du module.gradle script. Une bonne place est sous le plugin Gradle Android.

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

android {
    compileSdkVersion 21
    // etc.

build-processApplicationId.Grad

ci-dessous est la source de travail pour un script de construction res/xml/ placeholder. Une version mieux documentée est disponible sur github . Les améliorations et les ajouts sont les bienvenus.

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

les Chaînes.xml

à mon avis, il n'est pas nécessaire d'ajouter le support de placeholder pour les chaînes de ressources. Pour ces cas d'utilisation, au moins, il n'est pas nécessaire. Cependant, vous pouvez facilement modifier le script pour non seulement remplacer les espaces réservés dans le répertoire res/xml/, mais aussi dans le répertoire res/values/.

8
répondu Rob Meeuwisse 2014-11-13 19:51:42

je préférerais un mélange entre Cyril et rciovati. Je pense que c'est plus simple, vous n'avez que deux modifications.

le build.gradle ressemble à:

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

et le 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>
6
répondu icastell 2014-07-25 10:33:01

basé sur l'exemple de @ChristianMelchior, voici ma solution, qui résout deux problèmes dans les solutions précédentes:

  • solutions qui changent les valeurs.xml dans le répertoire de construction cause une reconstruction complète des ressources (y compris aapt de tous les drawables)

  • pour une raison inconnue, IntelliJ (et probablement Android Studio) ne traitent pas de manière fiable les ressources, provoquant la construction de contenir de l'onu-qui a remplacé le "151900920 du fournisseur" autorités

cette nouvelle solution fait les choses plus de la manière Grad en créant une nouvelle tâche et permet des constructions incrémentielles en définissant des fichiers d'entrée et de sortie.

  1. " créez un fichier (dans l'exemple je l'ai mis dans un répertoire variants ), formaté comme un fichier xml ressource, qui contient des ressources de chaîne. Ces seront fusionnés dans l'application des ressources, et tout la présence de .res-auto dans les valeurs sera remplacée par le nom du paquet de la variante, par exemple <string name="search_provider">.res-auto.MySearchProvider</string>

  2. ajouter le build_extras.gradle fichier de ce gist à votre projet et de référence à partir de la principale build.gradle en ajoutant apply from: './build_extras.gradle' quelque part au-dessus du android bloc

  3. assurez-vous de définir un nom de paquet par défaut en l'ajoutant au android.defaultConfig bloc de build.gradle

  4. dans AndroidManifest.xml et d'autres fichiers de configuration (comme xml/searchable.xml pour l'auto-complétion des fournisseurs de recherche), de référence du fournisseur (par exemple @string/search_provider )

  5. si vous devez obtenir le même nom, vous pouvez utiliser la variable BuildConfig.PACKAGE_NAME , par exemple BuildConfig.PACKAGE_NAME + ".MySearchProvider"

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


Maj: cette méthode ne fonctionne que sur Android 2.2.1 et plus tard. Pour les plates-formes plus anciennes, voir cette réponse , qui a son propre ensemble de problèmes, depuis la nouvelle fusion manifeste est encore très rugueuse sur les bords…
4
répondu Pierre-Luc Paour 2017-05-23 10:31:11

Grad.construire

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

authentificateur.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>

Code:

public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY;
public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
4
répondu maros136 2015-11-06 17:16:35

j'ai écrit un blogpost avec Github sample project qui aborde ce problème (et d'autres problèmes similaires) d'une manière légèrement différente de celle de Cyril.

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

3
répondu icecreamman 2013-08-27 18:57:03

malheureusement, la version actuelle (0.4.1) du plugin android ne semble pas fournir une bonne solution pour cela. Je n'ai pas eu le temps d'essayer encore, mais une solution possible à ce problème serait d'utiliser une ressource de chaîne @string/provider_authority , et de l'utiliser dans le manifeste: android:authority="@string/provider_authority" . Vous avez alors un res/values/provider.xml dans le dossier res de chaque type de construction qui devrait outrepasser l'autorité, dans votre cas ce serait src/debug/res

j'ai vérifié générer le fichier xml à la volée, mais encore une fois, il ne semble pas être une bonne crochets dans la version actuelle du plugin. Je recommande de mettre en place une requête de fonctionnalité cependant, je peux imaginer plus de gens vont se trouver dans cette même question.

2
répondu Marcus Forsell Stahre 2013-05-28 12:42:01

La réponse dans ce post fonctionne pour moi.

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

j'utilise 3 saveurs différentes donc je crée 3 manifestes avec fournisseur de contenu dans chaque saveur comme kevinrschultz dit:

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

    paid {
        packageName "your.package.name.paid"
    }

    other {
        packageName "your.package.name.other"
    }
}

votre principal manifeste ne comprend pas les fournisseurs:

<?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>

et votre manifeste dans votre chaque saveur y compris le fournisseur.

gratuit: 151960920"

<?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>

payé:

<?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>

autres:

<?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>
2
répondu jcmore2 2014-12-05 11:56:18

pourquoi ne pas ajouter ceci?

"151900920 de type".packageNameSuffix =". $type.nom "

0
répondu guydemossyrock 2014-02-14 12:40:27

ma solution est d'utiliser le remplacement des espaces dans AndroidManifest.xml . Il gère également les attributs packageNameSuffix de sorte que vous pouvez avoir debug et release ainsi que toute autre construction personnalisée sur le même appareil.

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

je l'ai sur un gist trop si vous voulez voir si elle évolue plus tard.

j'ai trouvé une approche plus élégante que les approches d'analyse de ressources multiples et XML.

0
répondu Saad Farooq 2014-04-04 02:53:25