with warning update tools studio replaced has configurar compile been and android gradle android-contentprovider android-gradle

android - warning - update gradle



Usar tipos de compilación en Gradle para ejecutar la misma aplicación que usa ContentProvider en un dispositivo (14)

¿Por qué no solo agregar esto?

type.packageNameSuffix = ". $ type.name"

Configuré Gradle para agregar el sufijo del nombre del paquete a mi aplicación de depuración para poder tener la versión de lanzamiento que estoy usando y la versión de depuración en un teléfono. Hacía referencia a esto: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types

Mi archivo build.gradle se ve así:

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

Todo funciona bien hasta que empiece a usar ContentProvider en mi aplicación. Yo obtengo:

Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]

Entiendo que esto sucede porque dos aplicaciones (versión y depuración) registran la misma autoridad de ContentProvider.

Veo una posibilidad para resolver esto. Si entiendo correctamente, deberías poder especificar diferentes archivos para usar al compilar. Entonces debería ser capaz de poner diferentes autoridades en diferentes archivos de recursos (y desde la autoridad del conjunto de Manifest como recurso de cadena) y decirle a Gradle que use un recurso diferente para la compilación de depuración. ¿Es eso posible? Si es así, ¡cualquier sugerencia sobre cómo lograr eso sería increíble!

¿O tal vez es posible modificar directamente Manifiesto usando Gradle? Cualquier otra solución sobre cómo ejecutar la misma aplicación con ContentProvider en un dispositivo siempre es bienvenida.


Basado en la muestra de @ChristianMelchior, esta es mi solución, que soluciona dos problemas en las soluciones anteriores:

  • Las soluciones que cambian values.xml en el directorio de compilación causan una reconstrucción completa de los recursos (incluido un apt de todos los objetos descartables)

  • por una razón desconocida, IntelliJ (y probablemente Android Studio) no procesa de manera confiable los recursos, lo que hace que la compilación contenga autoridades de proveedor .res-auto sin reemplazar

Esta nueva solución hace las cosas más a la manera de Gradle mediante la creación de una nueva tarea y permite compilaciones incrementales mediante la definición de archivos de entrada y salida.

  1. crear un archivo (en el ejemplo, lo puse en un directorio de variants ), formateado como un archivo xml de recursos, que contiene recursos de cadenas. Estos se fusionarán en los recursos de la aplicación y cualquier aparición de .res-auto en los valores se reemplazará por el nombre del paquete de la variante, por ejemplo <string name="search_provider">.res-auto.MySearchProvider</string>

  2. agregue el archivo build_extras.gradle de esta build_extras.gradle a su proyecto y haga referencia a él desde build.gradle principal agregando apply from: ''./build_extras.gradle'' algún lugar sobre el bloque de android

  3. asegúrese de establecer un nombre de paquete predeterminado agregándolo al bloque android.defaultConfig de build.gradle

  4. en AndroidManifest.xml y otros archivos de configuración (como xml/searchable.xml para proveedores de xml/searchable.xml de autocompletado), haga referencia al proveedor (por ejemplo @string/search_provider )

  5. si necesita obtener el mismo nombre, puede usar la variable BuildConfig.PACKAGE_NAME , por ejemplo BuildConfig.PACKAGE_NAME + ".MySearchProvider"

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

Actualización: este método solo funciona en Android 2.2.1 y versiones posteriores. Para plataformas anteriores, vea esta respuesta , que tiene su propio conjunto de problemas, ya que la nueva fusión de manifiestos sigue siendo muy difícil en los bordes ...

Dado que la versión de complemento 0.8.3 (en realidad 0.8.1 pero no funcionaba correctamente) puede definir recursos dentro del archivo de compilación así que esta podría ser una solución más limpia porque no necesita crear archivos de cadenas ni depuración / liberación adicionales carpetas.

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>



La respuesta en esta publicación funciona para mí.

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

Utilizo 3 sabores diferentes, así que creo 3 manifiestos con proveedor de contenido en cada sabor, como dijo kevinrschultz:

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

Su Manifiesto principal no incluye proveedores:

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

Y su manifestación en cada sabor, incluido el proveedor.

Gratis:

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

Pagado:

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

Otro:

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


Lamentablemente, la versión actual (0.4.1) del complemento de Android no parece proporcionar una buena solución para esto. No he tenido tiempo de probar esto todavía, pero una posible solución para este problema sería usar un recurso de @string/provider_authority , y usar eso en el manifiesto: android:authority="@string/provider_authority" . A continuación, tiene un res/values/provider.xml en la carpeta res de cada tipo de compilación que debe anular la autoridad, en su caso esto sería src/debug/res

He investigado la posibilidad de generar el archivo xml sobre la marcha, pero una vez más, no parece haber ningún buen gancho para él en la versión actual del complemento. Sin embargo, recomendaría presentar una solicitud de función. Puedo imaginar que más personas se encontrarán con este mismo problema.


Mi solución es usar reemplazos de marcador de posición en AndroidManifest.xml . También maneja los atributos packageNameSuffix para que pueda tener debug y release , así como también otras compilaciones personalizadas en el mismo dispositivo.

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

También me gist si quieres ver si evoluciona más tarde.

Encontré un enfoque más elegante que los enfoques de análisis de XML y recursos múltiples.


Ninguna de las respuestas existentes me satisfizo, sin embargo Liberty estaba cerca. Así que así es como lo estoy haciendo. Antes que nada, en este momento estoy trabajando con:

  • Android Studio Beta 0.8.2
  • Gradle plugin 0.12. +
  • Gradle 1.12

Mi objetivo es ejecutar la versión de Debug junto con la versión de Release en el mismo dispositivo utilizando el mismo ContentProvider .

En build.gradle del sufijo del conjunto de aplicaciones para Debug build:

buildTypes { debug { applicationIdSuffix ".debug" } }

En el archivo AndroidManifest.xml configure las propiedades android:authorities de su ContentProvider :

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

En su código establezca la propiedad AUTHORITY que se puede usar donde sea necesario en su implementación:

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

Sugerencia: antes era BuildConfig.PACKAGE_NAME

¡Eso es! Funcionará como un encanto. ¡Sigue leyendo si usas SyncAdapter!

Actualización para SyncAdapter (14.11.2014)

Una vez más comenzaré con mi configuración actual:

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

Básicamente, si necesita personalizar algunos valores para diferentes compilaciones, puede hacerlo desde el archivo build.gradle:

  • use buildConfigField para acceder desde la clase BuildConfig.java
  • use resValue para acceder desde los recursos, por ejemplo, @ string / your_value

Como alternativa para los recursos, puede crear directorios buildType o de sabores separados y anular los valores XML o valores dentro de ellos. Sin embargo, no voy a usarlo en el ejemplo a continuación.

Ejemplo

En el archivo build.gradle , agregue lo siguiente:

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

Verá resultados en la clase BuildConfig.java

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

y en 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>

En su authenticator.xml utilice el recurso especificado en el archivo 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" />

En su syncadapter.xml use el mismo recurso nuevamente y @ string / autoridades también

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

Sugerencia: la función de autocompletado (Ctrl + Espacio) no funciona para estos recursos generados, por lo que debe escribirlos manualmente


No sé si alguien lo menciona. En realidad, después de Android Gradle Plugin 0.10+, la fusión manifiesta proporcionará el soporte oficial para esta función: http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger

En AndroidManifest.xml, puede usar $ {packageName} de esta manera:

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

Y en tu build.gradle puedes tener:

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

Vea el ejemplo completo aquí: https://code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152

y aquí: https://code.google.com/p/anymemo/source/browse/build.gradle#41


Prefiero una mezcla entre Cyril y rciovati. Creo que es más simple, solo tienes dos modificaciones.

El build.gradle ve así:

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

Y el 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>


Si bien el ejemplo de Cyril funciona muy bien si solo tienes algunos tipos de compilación, se vuelve complicado rápidamente si tienes muchos tipos de compilación y / o sabores de productos, ya que necesitas mantener muchos AndroidManifest.xml diferentes.

Nuestro proyecto consta de 3 tipos de compilación y 6 sabores que suman 18 variantes de compilación, por lo que hemos añadido soporte para ".res-auto" en las autoridades de ContentProvider, que se expanden al nombre de paquete actual y elimina la necesidad de mantener diferentes 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) } }

El código de ejemplo se puede encontrar aquí: https://gist.github.com/cmelchior/6988275


Use los marcadores de posición ${applicationId} en xml y BuildConfig.APPLICATION_ID en el código.

Tendrá que extender el script de compilación para habilitar marcadores de posición en archivos xml que no sean el manifiesto. Puede usar un directorio de origen por variante de compilación para proporcionar diferentes versiones de los archivos xml, pero el mantenimiento se volverá engorroso muy rápidamente.

AndroidManifest.xml

Puede utilizar el marcador de posición de la aplicaciónId en el manifiesto. Declare a su proveedor así:

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

Tenga en cuenta el bit ${applicationId} . Esto se reemplaza durante el tiempo de compilación con el ID de aplicación real para la variante de compilación que se está creando.

En codigo

Su ContentProvider necesita construir la cadena de autoridad en el código. Puede usar la clase BuildConfig.

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

Tenga en cuenta el bit BuildConfig.APPLICATION_ID . Es una clase generada con el ID de aplicación real para la variante de compilación que se está construyendo.

res / xml / files, por ejemplo, syncadapter.xml, accountauthenticator.xml

Si desea utilizar un Adaptador de sincronización, deberá proporcionar metadatos para ContentProvider y AccountManager en archivos xml en el directorio res / xml /. El marcador de posición applicationId no es compatible aquí. Pero puede extender el script de compilación usted mismo para hackearlo.

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

Una vez más, tenga en cuenta ${applicationId} . Esto solo funciona si agrega el script de abajo a la raíz de su módulo y lo aplica desde build.gradle.

build.gradle

Aplique el script de compilación extra desde el script build.gradle del módulo. Un buen lugar está debajo del complemento gradle de Android.

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

build-processApplicationId.gradle

A continuación se muestra la fuente de trabajo para un script de construcción res / xml / placeholder. Una versión mejor documentada está disponible en github . Mejoras y extensiones son bienvenidas.

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

En mi opinión, no es necesario agregar soporte de marcadores para cadenas de recursos. Para el caso de uso anterior, al menos no es necesario. Sin embargo, podría cambiar fácilmente la secuencia de comandos para no solo reemplazar los marcadores de posición en el directorio res / xml /, sino también en el directorio res / values ​​/.


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>

Código:

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


Nueva sugerencia del sistema de compilación de Android: cambio de nombre de la autoridad de ContentProvider

Creo que todos ustedes han oído hablar del nuevo sistema de compilación basado en Android Gradle. Seamos honestos, este nuevo sistema de compilación es un gran paso adelante en comparación con el anterior. Todavía no es definitivo (al momento de escribir esto, la última versión es 0.4.2), pero ya puede usarlo de manera segura en la mayoría de sus proyectos.

Personalmente, cambié la mayor parte de mi proyecto a este nuevo sistema de compilación y tuve algunos problemas debido a la falta de soporte en algunas situaciones particulares. Uno de los cuales es el soporte para el cambio de nombre de autoridad de ContentProvider

El nuevo sistema construido con Android te permite tratar diferentes tipos de aplicaciones simplemente modificando el nombre del paquete en el momento de la compilación. Una de las principales ventajas de esta mejora es que ahora puede tener dos versiones diferentes de su aplicación instaladas en el mismo dispositivo al mismo tiempo. Por ejemplo:

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

Usando una configuración Gradle de este tipo, puede armar dos APK diferentes:

• Una APK de depuración con el nombre del paquete com.cyrilmottier.android.app.debug • Una versión APK con el nombre del paquete com.cyrilmottier.android.app

El único problema es que no podrá instalar los dos APK al mismo tiempo si ambos exponen un ContentProvider con las mismas autoridades. Es lógico que tengamos que cambiar el nombre de la autoridad en función del tipo de compilación actual ... pero esto no es compatible con el sistema de compilación de Gradle (¿todavía? ... Estoy seguro de que se solucionará pronto). Así que aquí hay un camino por recorrer:

Primero, debemos mover la declaración del proveedor de contenido manifiesto de Android del proveedor al tipo de compilación apropiado. Para hacer eso, simplemente tendremos:

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>

Asegúrese de eliminar la declaración ContentProvider de AndroidManifest.xml en src / main / porque Gradle no sabe cómo fusionar ContentProviders con el mismo nombre pero con una autoridad diferente.

Finalmente, podemos necesitar acceder a la autoridad en el código. Esto se puede hacer bastante fácilmente usando el archivo BuildConfig y el método 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 } } }

Gracias a esta solución alternativa, podrá usar BuildConfig.PROVIDER_AUTHORITY en su ProviderContract e instalar dos versiones diferentes de su aplicación al mismo tiempo.

Originalmente en Google+: https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ