2010-07-22 7 views
27

Я разрабатываю набор приложений, которые отличаются только в определенных брендингах (думаю, разные спортивные команды); однако я столкнулся с проблемой, когда я использую один проект библиотеки для всех специально заклейменных приложений и хочу использовать один и тот же ContentProvider для всех из них. Когда я создал ContentProvider, я объявил AUTHORITY как константу в классе (на пример кода примера), и я использую тот же самый авторитет в каждом конкретном приложении в файлах манифеста. Похоже, я не могу использовать один и тот же орган по каждому приложению, как я получаю эту ошибку при попытке установить второе приложение (я установить один брендированный один раз хорошо, но вторая установку):В нескольких приложениях используется один и тот же контент-провайдер

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

Я попробовал несколько подходов, но ни один из них, похоже, не работает. Одна из идей, которую я еще не сделал, заключалась в создании библиотеки jar и просто опускании класса Provider, который у меня есть, и его настройке в каждом конкретном приложении. Любые идеи о том, как обойти эту проблему, не прибегая к этому?

+0

Вы можете попробовать применить мое решение для аналогичной задачи: http: // stackoverflow.com/a/15964372/1220237 – Sash0k

ответ

17

ContentProviders идентифицируются авторитетом, поэтому он должен быть уникальным. Я не думаю, что в этом есть какие-то трюки.

Кроме того, на платформе Android есть ошибка, которая также предотвращает использование одного и того же имени класса для двух разных ContentProviders, даже если они имеют разные полномочия и содержатся в отдельных APK. См. Ошибку here.

Решение, которое я вам советую, заключается в создании абстрактного класса поставщика в проекте библиотеки и затем его расширении с уникальным именем в каждом отдельном приложении. Для этого вам, вероятно, понадобится создать скрипт для создания/изменения отдельных манифестатов и классов contentprovider.

Надеюсь, это поможет.

+0

Всякий раз, когда вы хотите расширить базовый набор функций Android, и предположите, что он будет работать (как его интуитивно понятный), вы узнаете, что это не так. – havexz

+1

Этот ответ больше не верен с более новой версией Android, поскольку Google разрешил проблему в 2014 году, как указано [здесь] (https://code.google.com/p/android/issues/detail?id=7716#c12) – Keridano

4

Допустит, ваш пакет библиотеки com.android.app.library свободного пакет com.android.app.free платного пакет com.android.app.paid

В свободном проекте и платный проект, сделать идентичный файл в пакете, который может быть что угодно, но он должен быть одна и та же.

Пример:

  1. Создать новый пакет в вашей бесплатной версии с com.android.app.data

  2. Создайте файл с именем Authority.java и внутри (Authority.java) поставил:

    public class Authority {

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

    }

  3. Повторите это для платной версии, не забудьте оставить имя пакета одинаковым и имя класса.

Теперь в файле контракта, в библиотеке используйте следующее:

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

Теперь вы должны быть в состоянии использовать две власти.

Кредит: Ян Warick (код подправить) Android - Having Provider authority in the app project Отказ от ответственности: Я разместил его здесь, а также: Android duplicate provider authority problem - Не уверен, что, если это разрешено ответить на тот же тип вопроса с такой же ответ.

1

Следующий способ может быть использован для упаковки ContentProvider в библиотеке и установки полномочий ContentProvider во время выполнения, чтобы он мог быть включен в несколько проектов без конфликта ContentProvider. Это работает, потому что реальный «авторитет» происходит от AndroidManifest ... не класса ContentProvider.

Начнем с основного ContentProvider implementation..AUTHORITY, CONTENT_URI и UriMatcher являются статическими, но не 'окончательный' ....

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

Затем переопределить метод 'AttachInfo', так что, когда ContentProvider сначала инициализируется, ваш ContentProvider будет вызываться с помощью ProviderInfo, который почерпнут из AndroidManifest. Это произойдет до того, как будут сделаны любые возможные запросы, скорее всего, во время начальной настройки класса приложения. Используйте эту возможность, чтобы сбросить значения AUTHORITY, CONTENT_URI и UriMatcher на их «реальные» значения, как это предусмотрено приложением, использующим библиотеку 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); 
} 

При запуске приложения, то ContentProvider фактически экземпляр вместе с классом Application, поэтому он будет иметь доступ ко всей необходимой информации пакета. объект ProviderInfo будет содержать информацию, представленную в AndroidManifest ... Список, включенный в окончательное приложение.

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

Орган теперь будет переписан с «com.foo.barapp.content» вместо значения по умолчанию, и UriMatcher будет обновлен до значения приложения, а не по умолчанию. Классы, которые полагаются на «AUTHORITY», теперь будут получать доступ к обновленному значению, а UriMatcher будет правильно различать входящие запросы для «com.foo.barapp.content».

Я тестировал это как с примерным приложением, так и с пакетом androidTest одновременно и нашел, что он работает правильно.

9

Это старый вопрос, но я недавно искал что-то подобное. С ароматами Build, это действительно прямо сейчас.

Укажите BuildConfigField в 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"' 
    } 

Укажите полномочия поставщика в манифесте:

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

Установите власть в поставщике, используя BuildConfigField Variable:

public static final String AUTHORITY = BuildConfig.AUTHORITY 
+0

Это должен быть новый принятый ответ. Хотя вы можете избежать создания 'buildConfigField', используя вместо этого' getApplicationContext(). GetPackageName() 'во время выполнения. –

1

ВЫ МОЖЕТЕ!

Как сказано в this post (которым объясняет, как Firebase инициализирует библиотеку, не давая ему контекст из метода Application#onCreate()), вы можете использовать заполнитель в манифесте, например:

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

Отличный ответ! Короткий, сладкий, и к делу! Спасибо! – Sakiboy

+0

На самом деле ... слишком короткий, не работает, если вы не знаете, что делаете. Я бы предложил этот ответ: https://stackoverflow.com/a/43444164/2371425 – Sakiboy

Смежные вопросы