Múltiples aplicaciones usan el mismo proveedor de contenido

Estoy desarrollando un conjunto de aplicaciones que se distinguen solo en ciertas marcas (piense en diferentes equipos deportivos); sin embargo, me encuentro con un problema en el que estoy usando un proyecto de biblioteca para todas las aplicaciones específicamente marcadas y quiero usar el mismo ContentProvider para todas ellas. Cuando creé el ContentProvider, declare AUTORIDAD como una constante en la clase (según el código de ejemplo dev) y estoy usando la misma autoridad en cada aplicación específica en los archivos de manifiesto. Parece que no puedo usar la misma autoridad en todas las aplicaciones ya que recibo este error al intentar instalar una segunda aplicación (instalo una marcada pero la segunda instalación):

WARN/PackageManager(66): Can't install because provider name com.xxx.Provider (in package com.xxx) is already used by com.zzz 

He intentado varios enfoques, pero ninguno de ellos parece funcionar. Una idea que aún no he hecho, fue crear un archivo jar de la biblioteca y simplemente omitir la clase Provider que tengo y personalizarla en cada aplicación específica. ¿Alguna idea sobre cómo solucionar este problema sin recurrir a eso?

ContentProviders son identificados por la autoridad, por lo que debe ser único. No creo que haya ningún truco en eso.

Además, hay un error en la plataforma de Android que también impide usar el mismo nombre de clase para dos ContentProviders diferentes, incluso si tienen autoridad diferente y están contenidos en APK por separado. Vea el error aquí .

La solución que recomiendo para usted es crear la clase de proveedor abstracta en su proyecto de biblioteca y luego extenderla con un nombre único en cada una de las aplicaciones individuales. Para hacer esto práctico, probablemente necesite crear un script para generar / modificar los manifiestos individuales y las clases de proveedor de contenido.

Espero que esto ayude.

Es una vieja pregunta, pero estaba buscando hacer algo similar recientemente. Con los sabores Build, es realmente sencillo ahora.

Especifique BuildConfigField en el archivo gradle:

  productFlavors { free { applicationId "com.example.free" buildConfigField 'String', 'AUTHORITY', '"com.example.free.contentprovider"' } paid { applicationId "com.example.paid" buildConfigField 'String', 'AUTHORITY', '"com.example.paid.contentprovider"' } 

Especifique la autoridad del proveedor en el manifiesto:

   

Establezca la autoridad en el proveedor que usa la variable BuildConfigField:

  public static final String AUTHORITY = BuildConfig.AUTHORITY 

Digamos que su paquete de biblioteca es com.android.app.library el paquete gratuito es com.android.app.free paquete pagado es com.android.app.paid

En su proyecto gratuito y proyecto pago, haga un archivo idéntico en un paquete que puede ser cualquier cosa, pero debe ser el mismo.

Ejemplo:

  1. Cree un nuevo paquete en su versión gratuita con com.android.app.data

  2. Cree un archivo llamado Authority.java y dentro (Authority.java) ponga:

    public class Authority {

     `public static final String CONTENT_AUTHORITY = "YOUR PROVIDER";` 

    }

  3. Repita esto para la versión paga, recuerde mantener el nombre del paquete igual y el nombre de la clase.

Ahora, en su archivo de contrato, en su biblioteca, use lo siguiente:

 public static String AUTHORITY = initAuthority(); private static String initAuthority() { String authority = "something.went.wrong.if.this.is.used"; try { ClassLoader loader = Contract.class.getClassLoader(); Class clz = loader.loadClass("com.android.app.data.Authority"); Field declaredField = clz.getDeclaredField("CONTENT_AUTHORITY"); authority = declaredField.get(null).toString(); } catch (ClassNotFoundException e) {} catch (NoSuchFieldException e) {} catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } return authority; } public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); 

Ahora deberías poder usar dos autoridades.

Crédito: Ian Warick (redacción del código) Android – Tener autoridad del proveedor en el proyecto de la aplicación Descargo de responsabilidad: Lo publiqué también aquí: Problema de autoridad del proveedor duplicado de Android – No estoy seguro de poder contestar el mismo tipo de pregunta con la misma respuesta.

¡USTED PUEDE!

Como se dijo en esta publicación (que explica cómo Firebase inicializa su biblioteca sin darle un contexto de su método Application#onCreate() ), puede usar un marcador de posición en su manifiesto, como este:

   

La siguiente forma se puede usar para empaquetar un ContentProvider dentro de una biblioteca y establecer la autoridad de ContentProvider en tiempo de ejecución, de modo que se pueda incluir en varios proyectos sin el conflicto de ContentProvider Authority. Esto funciona porque la ‘autoridad’ real proviene del AndroidManifest … no de la clase ContentProvider.

Comience con la implementación básica de ContentProvider … AUTHORITY, CONTENT_URI y UriMatcher son estáticos, pero no ‘finales’ …

 public class MyContentProvider extends ContentProvider { public static String AUTHORITY = "com.foo.bar.content"; public static Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); protected static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 

Luego, anule el método ‘attachInfo’, de modo que cuando se inicialice ContentProvider por primera vez, se llamará a su ContentProvider con el ProviderInfo recostackdo de AndroidManifest. Esto ocurrirá ANTES de que se realicen las posibles consultas, muy probablemente durante la configuración inicial de la clase de aplicación. Aproveche esta oportunidad para restablecer AUTHORITY, CONTENT_URI y UriMatcher a sus valores “reales”, tal como lo proporciona la Aplicación que está utilizando la biblioteca ContentProvider.

  @Override public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); AUTHORITY = info.authority; CONTENT_URI = Uri.parse("content://" + AUTHORITY); uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, AlarmTable.TABLENAME, ALARMS); uriMatcher.addURI(AUTHORITY, AttributeTable.TABLENAME, ATTRIBUTES); uriMatcher.addURI(AUTHORITY, DeepLinkTable.TABLENAME, DEEPLINKS); uriMatcher.addURI(AUTHORITY, NotificationTable.TABLENAME, NOTIFICATIONS); uriMatcher.addURI(AUTHORITY, MetaDataTable.TABLENAME, RESOURCE_METADATA); uriMatcher.addURI(AUTHORITY, ResourceTable.TABLENAME, RESOURCES); uriMatcher.addURI(AUTHORITY, ResourceAttributeTable.TABLENAME, RESOURCES_ATTRIBUTES); uriMatcher.addURI(AUTHORITY, ResourceTagTable.TABLENAME, RESOURCES_TAGS); uriMatcher.addURI(AUTHORITY, TagTable.TABLENAME, TAGS); uriMatcher.addURI(AUTHORITY, UserTagTable.TABLENAME, USER_TAGS); uriMatcher.addURI(AUTHORITY, UserTable.TABLENAME, USERS); uriMatcher.addURI(AUTHORITY, CUSTOM, RAW); } 

Cuando se inicia la aplicación, ContentProvider se instancia realmente junto con la clase de la aplicación, por lo que tendrá acceso a toda la información del paquete requerido. el objeto ProviderInfo contendrá la información proporcionada en el AndroidManifest … La lista incluida en la Aplicación final.

   

La Autoridad se reescribirá ahora con “com.foo.barapp.content” en lugar del valor predeterminado, y el UriMatcher se actualizará con el valor de la aplicación en lugar del valor predeterminado. Las clases que dependen de la “AUTORIDAD” ahora accederán al valor actualizado, y el UriMatcher distinguirá correctamente las consultas entrantes para el ‘com.foo.barapp.content’.

Probé esto con una aplicación de muestra y un paquete androidTest simultáneamente y encontré que funcionaba correctamente.