В нескольких приложениях используется один и тот же контент-провайдер
Я разрабатываю набор приложений, которые отличаются только определенным брендингом (думаю, разные спортивные команды); однако я столкнулся с проблемой, когда я использую один проект библиотеки для всех специально заклейменных приложений и хочу использовать один и тот же ContentProvider для всех из них. Когда я создал ContentProvider, я объявил AUTHORITY как константу в classе (на пример кода примера), и я использую тот же самый авторитет в каждом конкретном приложении в файлах манифеста. Похоже, я не могу использовать один и тот же авторитет для каждого приложения, поскольку получаю эту ошибку при попытке установить второе приложение (я устанавливаю один брендированный только отлично, а второй устанавливаю):
WARN/PackageManager(66): Can't install because provider name com.xxx.Provider (in package com.xxx) is already used by com.zzz
Я пробовал несколько подходов, но никто из них, похоже, не работает. Одна из идей, которую я еще не сделал, заключалась в создании библиотеки jar и просто опускании classа Provider, который у меня есть, и его настройке в каждом конкретном приложении. Любые идеи о том, как обойти эту проблему, не прибегая к этому?
- Создание и совместное использование файла из внутреннего хранилища
- Какова семантика withValueBackReference?
- Установка показывает ошибку в консоли: УСТАНАВЛИВАЙТЕ НЕИСПРАВНОСТЬ ПОСТАВЩИКА КОНФЛИКТА
- SyncAdapter без ContentProvider
- Когда использовать поставщика контента
ContentProviders идентифицируются авторитетом, поэтому он должен быть уникальным. Я не думаю, что в этом есть какие-то трюки.
Кроме того, в платформе Android есть ошибка, которая также предотвращает использование одного и того же имени classа для двух разных ContentProviders, даже если они имеют разные полномочия и содержатся в отдельных APK. См. Здесь ошибку.
Решение, которое я вам советую, заключается в создании абстрактного classа поставщика в проекте библиотеки, а затем его расширении с уникальным именем в каждом из отдельных приложений. Для этого вам, вероятно, понадобится создать скрипт для создания / изменения отдельных манифестатов и classов contentprovider.
Надеюсь это поможет.
Это старый вопрос, но я недавно искал что-то подобное. С ароматами 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"' }
Укажите полномочия поставщика в манифесте:
Установите полномочия поставщика, используя переменную BuildConfigField:
public static final String AUTHORITY = BuildConfig.AUTHORITY
Допустим, ваш пакет для библиотеки – com.android.app.library
бесплатный пакет com.android.app.free
заплаченный пакет com.android.app.paid
В своем бесплатном проекте и оплачиваемом проекте создайте идентичный файл в пакете, который может быть чем угодно, но должен быть тем же.
Пример:
-
Создайте новый пакет в своей бесплатной версии с помощью com.android.app.data
-
Создайте файл с именем Authority.java и внутри (Authority.java):
public class Authority {
`public static final String CONTENT_AUTHORITY = "YOUR PROVIDER";`
}
-
Повторите это для платной версии, не забудьте сохранить имя пакета одинаковым и имя classа.
Теперь в вашем файле контракта в вашей библиотеке используйте следующее:
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);
Теперь вы должны иметь возможность использовать два органа.
Кредит: Ian Warick (напишите код) Android – Владелец провайдера в проекте приложения. Отказ от ответственности. Я также разместил его здесь: проблема с полномочиями провайдера дубликатов Android. Не уверен, разрешено ли отвечать на один и тот же вопрос с тем же ответом.
ТЫ МОЖЕШЬ!
Как сказано в этом сообщении (объясняет, как Firebase инициализирует свою библиотеку, не предоставляя ей контекст из вашего метода Application#onCreate()
), вы можете использовать placeholder в своем манифесте, например:
Следующий способ можно использовать для пакета ContentProvider в библиотеке и установить полномочия ContentProvider во время выполнения, чтобы он мог быть включен в несколько проектов без конфликта ContentProvider. Это работает, потому что реальный «авторитет» происходит от AndroidManifest … не classа ContentProvider.
Начните с основной реализации ContentProvider. НАРУШЕНИЯ, 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. Это произойдет до того, как будут сделаны любые возможные запросы, скорее всего, во время начальной настройки classа приложения. Используйте эту возможность, чтобы сбросить значения 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 фактически создается вместе с classом Application, поэтому он будет иметь доступ ко всей необходимой информации о пакете. объект ProviderInfo будет содержать информацию, представленную в AndroidManifest … Список, включенный в окончательное приложение.
Теперь администратор будет переписан с «com.foo.barapp.content» вместо значения по умолчанию, а UriMatcher будет обновлен до значения приложения, а не по умолчанию. Классы, которые полагаются на «AUTHORITY», теперь будут получать доступ к обновленному значению, а UriMatcher будет правильно различать входящие запросы для «com.foo.barapp.content».
Я тестировал это как с примерным приложением, так и с пакетом androidTest одновременно и нашел, что он работает правильно.