2010-07-22 17 views
27

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 a través de cada aplicación como me sale este error al intentar instalar una segunda aplicación (instalo una marca de uno muy bien, pero la segunda instalo):Múltiples aplicaciones usan el mismo proveedor de contenido

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

tengo intentó 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?

+0

Puede tratar de aplicar mi solución para una tarea similar: http: // stackoverflow.com/a/15964372/1220237 – Sash0k

Respuesta

17

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

Además, hay un error en la plataforma 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. Ver el error here.

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.

+0

Siempre que desee extender el conjunto de funciones básicas de android y suponga que funcionará (como es intuitivo), se dará cuenta de que no es así. – havexz

+1

Esta respuesta ya no es cierta con la versión más reciente de Android, porque Google resolvió el problema en 2014 como se indica [aquí] (https://code.google.com/p/android/issues/detail?id=7716#c12) – Keridano

4

permite decir que su paquete de biblioteca es com.android.app.library paquete gratuito se com.android.app.free paquete de pago es com.android.app.paid

En su proyecto libre y proyecto pagados, crea un archivo idéntico en un paquete que puede ser cualquier cosa, pero debe ser el mismo.

Ejemplo:

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

  2. Crear un archivo llamado Authority.java y en el interior (Authority.java) puso:

    public class Authority {

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

    }

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

Ahora, en su expediente de contratación, en la biblioteca de utilizar 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 usted debería ser capaz de utilizar dos autoridades.

Crédito: Ian Warick (código de escritura hacia arriba) Android - Having Provider authority in the app project responsabilidad: he publicado aquí también: Android duplicate provider authority problem - No estoy seguro si deja de responder al mismo tipo de pregunta con la misma respuesta.

1

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

Comience con lo básico ContentProvider implementation..AUTHORITY, CONTENT_URI y UriMatcher son estáticos, pero no 'final' ....

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

A continuación, reemplazar el método 'AttachInfo', de modo que cuando el ContentProvider primero se inicializa, se llamará a su ContentProvider con ProviderInfo que se obtuvo 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, el ContentProvider es en realidad una instancia junto con la clase de aplicaciones, 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.

 <provider android:authorities="com.foo.barapp.content" 
       android:name="com.foo.bar.MyContentProvider"/> 

La Autoridad de ahora será reescrito con "com.foo.barapp.content" en lugar del valor por defecto, y el UriMatcher se actualizará con el valor de la aplicación en lugar del 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'.

He probado esto con una aplicación de muestra y un paquete androidTest simultáneamente y he encontrado que funciona correctamente.

9

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

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

especificará la autoridad proveedor en el manifiesto:

<provider 
     android:name=".ContentProvider" 
     android:authorities="${applicationId}.contentprovider" /> 

Establecer la autoridad en el proveedor utilizando la variable BuildConfigField:

public static final String AUTHORITY = BuildConfig.AUTHORITY 
+0

Esta debería ser la nueva respuesta aceptada. Aunque puede evitar crear 'buildConfigField' utilizando en su lugar' getApplicationContext(). GetPackageName() 'en tiempo de ejecución. –

1

¡PUEDES!

Como se ha dicho en this post (wich explica cómo Firebase inicializa su biblioteca sin darle un contexto de su método Application#onCreate()), se puede utilizar un marcador de posición en su manifiesto, como esto:

<provider 
     android:authorities="${applicationId}.yourcontentprovider" 
     android:name=".YourContentProvider" /> 
+0

¡Gran respuesta! ¡Corto, dulce y al punto! ¡Gracias! – Sakiboy

+0

En realidad ... demasiado corto, no funciona a menos que sepa lo que está haciendo. Sugeriría esta respuesta: https://stackoverflow.com/a/43444164/2371425 – Sakiboy

Cuestiones relacionadas