Подтвердить что ты не робот

В нескольких приложениях используется один и тот же контент-провайдер

Я разрабатываю набор приложений, которые различаются только в определенных брендингах (думаю, разные спортивные команды); однако я столкнулся с проблемой, когда я использую один проект библиотеки для всех специально заклейменных приложений и хочу использовать один и тот же 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, который у меня есть, и его настройке в каждом конкретном приложении. Любые идеи о том, как обойти эту проблему, не прибегая к этому?

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

Давайте скажем, что ваш пакет библиотеки com.android.app.library бесплатный пакет com.android.app.free платный пакет com.android.app.paid

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

Пример:

  • Создайте новый пакет в своей бесплатной версии с помощью com.android.app.data​​p >

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

    public class Authority {

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

    }

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

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

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 - наличие полномочий поставщика в проекте приложения Отказ от ответственности: я также разместил его здесь: Проблема с двойными полномочиями провайдера Android - Не уверен, разрешено ли отвечать на один и тот же вопрос с тем же ответом.

Ответ 4

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

Начните с основной реализации ContentProvider. AUTHORITY, CONTENT_URI и UriMatcher являются статическими, но не "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);

Затем переопределите метод 'attachInfo', чтобы при первом инициализации ContentProvider ваш ContentProvider вызывается с помощью ProviderInfo, который почерпнул из AndroidManifest. Это произойдет до того, как будут сделаны любые возможные запросы, скорее всего, во время начальной настройки класса приложения. Используйте эту возможность для reset 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 одновременно и нашел, что он работает правильно.

Ответ 5

ВЫ МОЖЕТЕ!

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

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