J'ai configuré Gradle pour ajouter le suffixe du nom du package à mon application de débogage afin que je puisse disposer de la version que j'utilise et de la version de débogage sur un seul téléphone. Je faisais référence à ceci: http://tools.Android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types
Mon fichier build.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 reçois:
Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]
Je comprends que cela se produit car deux applications (release et debug) enregistrent la même autorité ContentProvider.
Je vois une possibilité pour résoudre ce problème. Si je comprends bien, vous devriez pouvoir spécifier différents fichiers à utiliser lors de la construction. Ensuite, je devrais pouvoir mettre différentes autorités dans différents fichiers de ressources (et à partir de l'autorité de l'ensemble de manifestes en tant que ressource de chaîne) et indiquer à Gradle d'utiliser différentes ressources pour la construction du débogage. Est-ce possible? Si oui, alors des astuces pour y parvenir seraient géniales!
Ou peut-être est-il possible de modifier directement Manifest à l'aide de Gradle? Toute autre solution permettant d’exécuter la même application avec ContentProvider sur un seul appareil est toujours la bienvenue.
Aucune des réponses existantes ne me satisfaisait, cependant Liberty était proche. Alors voici comment je le fais. Tout d’abord au moment où je travaille avec:
Mon objectif est d'exécuter Debug
version avec Release
version sur le même périphérique en utilisant le même ContentProvider
.
Dans build.gradle de votre suffixe de jeu d'applications pour la construction de débogage:
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
Dans AndroidManifest.xml ensemble de fichiers 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 définissez AUTHORITY
propriété qui peut être utilisée à tout moment dans votre implémentation:
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
Astuce: Avant c'était BuildConfig.PACKAGE_NAME
Une fois encore, je vais commencer par ma configuration actuelle:
En gros, si vous avez besoin de personnaliser certaines valeurs pour différentes versions, vous pouvez le faire à partir du fichier build.gradle:
BuildConfig.Java
classeEn guise d'alternative pour les ressources, vous pouvez créer des répertoires buildType ou flavour distincts et remplacer les XML ou les valeurs qu'ils contiennent. Cependant, je ne vais pas l'utiliser dans l'exemple ci-dessous.
Dans le fichier build.gradle, ajoutez ce qui suit:
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 class
public static final String ACCOUNT_TYPE = "your.syncadapter.type.debug";
et dans construire/généré/res/généré/debug/values / généré.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 authenticator.xml, utilisez la ressource spécifiée dans le fichier 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"
/>
Dans votre syncadapter.xml, utilisez à nouveau la même ressource et @ string/autorités aussi
<?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"
/>
Astuce: La complétion automatique (Ctrl + Espace) ne fonctionne pas pour ces ressources générées, vous devez donc les taper manuellement
Nouveau conseil système Android: Renommer l’autorité ContentProvider
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 énorme pas en avant par rapport au précédent. Ce n'est pas encore définitif (au moment d'écrire ces lignes, la dernière version est la 0.4.2), mais vous pouvez déjà l'utiliser en toute sécurité dans la plupart de vos projets.
J'ai personnellement transféré l'essentiel de mon projet vers ce nouveau système de construction et j'ai eu quelques problèmes en raison du manque de support dans certaines situations particulières. L’un d’eux est le support de l’autorité ContentProvider de renommer
Le nouveau système construit Android vous permet de traiter différents types de votre application en modifiant simplement le nom du package au moment de la compilation. L'un des principaux avantages de cette amélioration est que vous pouvez désormais installer deux versions différentes de votre application 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 fichiers APK différents:
• Un APK de débogage avec le nom du paquet com.cyrilmottier.Android.app.debug • Un APK de version avec le nom du paquet com.cyrilmottier.Android.app
Le seul problème avec cela est que vous ne pourrez pas installer les deux fichiers APK en même temps s'ils exposent un fournisseur de contenu avec les mêmes autorités. Logiquement, nous devons renommer l'autorité en fonction du type de génération actuel… mais cela n'est pas pris en charge par le système de génération Gradle (pour le moment? ... je suis sûr que cela sera corrigé prochainement). Donc, voici un chemin à parcourir:
Premièrement, nous devons déplacer la déclaration ContentProvider du fournisseur Android vers le type de construction approprié. Pour ce faire, nous aurons 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/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>
Assurez-vous de supprimer la déclaration ContentProvider du fichier AndroidManifest.xml dans src/main/car Gradle ne sait pas comment fusionner ContentProviders ayant le même nom mais une autorité différente.
Enfin, nous aurons peut-être besoin d'accéder à l'autorité dans le 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 de contournement, vous pourrez utiliser BuildConfig.PROVIDER_AUTHORITY dans votre contrat ProviderContract et installer deux versions différentes de votre application simultanément.
À l'origine sur Google+: https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ
Bien que l'exemple de Cyril fonctionne très bien si vous ne possédez que quelques types de version, il devient rapidement compliqué si vous avez plusieurs types de version et/ou types de produits, car vous devez gérer de nombreux fichiers AndroidManifest.xml différents.
Notre projet se compose de 3 types de construction différents et de 6 versions différentes, totalisant 18 variantes de construction. Nous avons donc ajouté la prise en charge de ".res-auto" dans les autorisations ContentProvider, qui s'étend au nom de package actuel et supprime la nécessité de gérer un autre fichier 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
Depuis la version 0.8.3 du plugin (en réalité 0.8.1 mais elle ne fonctionnait pas correctement), vous pouvez définir des ressources dans le fichier de construction. Il pourrait donc s'agir d'une solution plus propre, car vous n'avez pas besoin de créer de fichiers de chaînes ni de débogage/publication supplémentaire. Dossiers.
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>
Je ne sais pas si quelqu'un en parle. En fait, après Android gradle plugin 0.10+, la fusion de manifestes fournira le support officiel pour 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 build.gradle, vous pouvez avoir:
productFlavors {
free {
packageName "org.pkg1"
}
pro {
packageName "org.pkg2"
}
}
Voir l'exemple complet 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
Utilisation ${applicationId}
espaces réservés en xml et BuildConfig.APPLICATION_ID
dans du code.
Vous devrez étendre le script de génération 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 très fastidieuse.
AndroidManifest.xml
Vous pouvez utiliser l’espace réservé applicationId prêt à l'emploi dans le manifeste. Déclarez votre fournisseur comme ceci:
<provider
Android:name=".provider.DatabaseProvider"
Android:authorities="${applicationId}.DatabaseProvider"
Android:exported="false" />
Noter la ${applicationId}
peu. Celui-ci est remplacé au moment de la construction par l'ID application réel de la variante de construction en cours de construction.
En code
Votre fournisseur de contenu doit créer la chaîne d'autorité dans le 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";
// ...
}
Noter la BuildConfig.APPLICATION_ID
peu. Il s'agit d'une classe générée avec l'identificateur d'application actuel pour la variante de construction en cours de construction.
res/xml/files, par exemple. syncadapter.xml, accountauthenticator.xml
Si vous souhaitez utiliser un adaptateur de synchronisation, vous devez fournir des métadonnées pour ContentProvider et AccountManager dans des fichiers xml dans le répertoire res/xml /. L'espace réservé applicationId n'est pas pris en charge ici. Mais vous pouvez étendre le script de compilation 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" />
Encore une fois, notez le ${applicationId}
. Cela ne fonctionne que si vous ajoutez le script ci-dessous à la racine de votre module et l'appliquez à partir de build.gradle.
build.gradle
Appliquez le script de construction supplémentaire à partir du script de module build.gradle. Un bon endroit est en dessous du plugin Android gradle.
apply plugin: 'com.Android.application'
apply from: './build-processApplicationId.gradle'
Android {
compileSdkVersion 21
// etc.
build-processApplicationId.gradle
Vous trouverez ci-dessous une source de travail pour un script de construction res/xml/placeholder. Une version mieux documentée est disponible sur github . Les améliorations et extensions sont les bienvenues.
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
À mon avis, il n'est pas nécessaire d'ajouter un support d'espace réservé pour les chaînes de ressources. Pour le cas d'utilisation ci-dessus au moins, ce n'est pas nécessaire. Cependant, vous pouvez facilement changer le script pour remplacer non seulement les espaces réservés dans le répertoire res/xml /, mais également dans le répertoire res/values /.
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 à ceci:
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>
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>
Code:
public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY;
public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
J'ai écrit un exemple de projet blogpost avec Github 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
Malheureusement, la version actuelle (0.4.1) du plugin Android ne semble pas être une bonne solution à cela. Je n'ai pas encore eu le temps de l'essayer, mais une solution de contournement possible pour ce problème serait d'utiliser une ressource de chaîne @string/provider_authority
, et utilisez-le 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 doit remplacer l’autorité; dans votre cas, il s’agirait de src/debug/res
J'ai examiné la possibilité de générer le fichier XML à la volée, mais encore une fois, il ne semble pas y avoir de bon point d'ancrage pour celui-ci dans la version actuelle du plugin. Je recommanderais toutefois de faire une demande de fonctionnalité, je peux imaginer que plus de personnes se heurteront au même problème.
La réponse dans ce post fonctionne pour moi.
J'utilise 3 saveurs différentes, je crée donc 3 manifestes avec le fournisseur de contenu dans chaque saveur, comme le dit kevinrschultz:
productFlavors {
free {
packageName "your.package.name.free"
}
paid {
packageName "your.package.name.paid"
}
other {
packageName "your.package.name.other"
}
}
Votre principal manifeste n'inclut 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.
Libre:
<?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>
Autre:
<?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>
Ma solution consiste à utiliser un espace réservé de remplacement dans AndroidManifest.xml
. Il gère également les attributs packageNameSuffix
afin que vous puissiez avoir debug
et release
ainsi que toute autre génération personnalisée sur le même périphérique.
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
aussi si vous voulez voir si cela évolue plus tard.
J'ai trouvé une approche plus élégante que les multiples ressources et les méthodes d'analyse XML.
Pourquoi ne pas simplement ajouter ceci?
type.packageNameSuffix = ". $ type.name"