Несколько приложений используют одного поставщика контента - PullRequest
37 голосов
/ 22 июля 2010

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

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

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

Ответы [ 5 ]

25 голосов
/ 21 августа 2016

Это старый вопрос, но я недавно пытался сделать что-то подобное. Со вкусами 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:

    public static final String AUTHORITY = BuildConfig.AUTHORITY
20 голосов
/ 28 мая 2011

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

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

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

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

5 голосов
/ 31 января 2017

ВЫ МОЖЕТЕ!

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

    <provider
         android:authorities="${applicationId}.yourcontentprovider"
         android:name=".YourContentProvider" />
4 голосов
/ 12 января 2014

Допустим, ваш библиотечный пакет 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);

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

Кредит: Ян Уорик (запись кода) Android - Наличие полномочий провайдера в проекте приложения Отказ от ответственности: Iтакже опубликовал это здесь: Проблема с полномочиями поставщика дубликатов Android - Не уверен, разрешено ли отвечать на тот же вопросвопрос с тем же ответом.

1 голос
/ 09 августа 2016

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

Начните с базовой реализации ContentProvider. 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. Это будет происходить ПЕРЕД любыми возможными запросами, скорее всего, во время начальной настройки класса Application. Используйте эту возможность, чтобы сбросить 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 и обнаружил, что он работает правильно.

...