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.
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>
agregue el archivo
build_extras.gradle
de estabuild_extras.gradle
a su proyecto y haga referencia a él desdebuild.gradle
principal agregandoapply from: ''./build_extras.gradle''
algún lugar sobre el bloque deandroid
asegúrese de establecer un nombre de paquete predeterminado agregándolo al bloque
android.defaultConfig
debuild.gradle
en
AndroidManifest.xml
y otros archivos de configuración (comoxml/searchable.xml
para proveedores dexml/searchable.xml
de autocompletado), haga referencia al proveedor (por ejemplo@string/search_provider
)si necesita obtener el mismo nombre, puede usar la variable
BuildConfig.PACKAGE_NAME
, por ejemploBuildConfig.PACKAGE_NAME + ".MySearchProvider"
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>
He escrito un blogpost con el proyecto de ejemplo Github que aborda este problema (y otros problemas similares) de una manera ligeramente diferente a la de Cyril.
http://brad-android.blogspot.com/2013/08/android-gradle-building-unique-build.html
La respuesta en esta publicación funciona para mí.
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