Использование типов сборки в Gradle для запуска того же приложения, которое использует ContentProvider на одном устройстве
Я установил Gradle, чтобы добавить суффикс имени пакета в мое приложение для отладки, чтобы у меня была версия выпуска, которую я использую и отлаживаю версию на одном телефоне. Я ссылался на это: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types
Мой файл build.gradle выглядит следующим образом:
...
android
{
...
buildTypes
{
debug
{
packageNameSuffix ".debug"
versionNameSuffix " debug"
}
}
}
Все работает нормально, пока я не начну использовать ContentProvider в своем приложении. Я получаю:
Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]
Я понимаю, что это происходит из-за того, что два приложения (выпуск и отладка) регистрируют один и тот же авторитет ContentProvider.
Я вижу одну возможность решить эту проблему. Если я правильно понимаю, вы должны указать разные файлы, которые будут использоваться при создании. Затем я должен иметь возможность устанавливать различные полномочия в разных файлах ресурсов (и из Manifest set authority в качестве строкового ресурса) и сообщить Gradle использовать другой ресурс для сборки отладки. Это возможно? Если да, то любые подсказки о том, как достичь этого, были бы замечательными!
Или, возможно, можно напрямую модифицировать манифест с помощью Gradle? Любое другое решение о том, как запускать такое приложение с ContentProvider на одном устройстве, всегда приветствуется.
Ответы
Ответ 1
Ни один из существующих ответов не удовлетворил меня, однако Свобода была близка. Так вот как я это делаю.
Прежде всего в настоящий момент я работаю с:
- Android Studio Beta 0.8.2
- Gradle плагин 0.12. +
- Gradle 1.12
Моя цель - запустить версию Debug
вместе с версией Release
на том же устройстве, используя тот же ContentProvider
.
В build.gradle вашего суффикса набора приложений для сборки Debug:
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
В файле AndroidManifest.xml установите android:authorities
свойство вашего ContentProvider
:
<provider
android:name="com.example.app.YourProvider"
android:authorities="${applicationId}.provider"
android:enabled="true"
android:exported="false" >
</provider>
В свой код установите свойство AUTHORITY
, которое можно использовать везде, где это необходимо в вашей реализации:
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
Совет: До того, как он был BuildConfig.PACKAGE_NAME
Что это! Он будет работать как шарм. Продолжайте читать, если вы используете SyncAdapter!
Обновление для SyncAdapter (14.11.2014)
Еще раз я начну с текущей настройки:
- Android Studio Beta 0.9.2
- Gradle плагин 0.14.1
- Gradle 2.1
В принципе, если вам нужно настроить некоторые значения для разных сборников, вы можете сделать это из файла build.gradle:
- используйте buildConfigField для доступа к нему из класса
BuildConfig.java
- используйте resValue для доступа к нему из ресурсов, например. @string/your_value
В качестве альтернативы ресурсам вы можете создавать отдельные каталоги buildType или аромата и переопределять XML файлы или значения внутри них. Однако я не буду использовать его в приведенном ниже примере.
Пример
В файле build.gradle добавьте следующее:
defaultConfig {
resValue "string", "your_authorities", applicationId + '.provider'
resValue "string", "account_type", "your.syncadapter.type"
buildConfigField "String", "ACCOUNT_TYPE", '"your.syncadapter.type"'
}
buildTypes {
debug {
applicationIdSuffix ".debug"
resValue "string", "your_authorities", defaultConfig.applicationId + '.debug.provider'
resValue "string", "account_type", "your.syncadapter.type.debug"
buildConfigField "String", "ACCOUNT_TYPE", '"your.syncadapter.type.debug"'
}
}
Вы увидите результаты в классе BuildConfig.java
public static final String ACCOUNT_TYPE = "your.syncadapter.type.debug";
и в сборке/сгенерированном/res/сгенерированном/debug/values /generated.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from default config. -->
<item name="account_type" type="string">your.syncadapter.type.debug</item>
<item name="authorities" type="string">com.example.app.provider</item>
</resources>
В authenticator.xml используйте ресурс, указанный в файле build.gradle
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/account_type"
android:icon="@drawable/ic_launcher"
android:smallIcon="@drawable/ic_launcher"
android:label="@string/app_name"
/>
В файле syncadapter.xml снова используйте тот же ресурс и @string/authority тоже
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="@string/authorities"
android:accountType="@string/account_type"
android:userVisible="true"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
/>
Совет: autocompletion (Ctrl + Space) не работает для этих сгенерированных ресурсов, поэтому вам нужно вводить их вручную
Ответ 2
Новый совет системы Android: управление правами ContentProvider
Я думаю, вы все слышали о новой системе сборки на базе Android Gradle. Пусть честно, эта новая система сборки - огромный шаг вперед по сравнению с предыдущей. Это еще не окончательно (начиная с этой записи, последняя версия 0.4.2), но вы уже можете безопасно использовать ее в большинстве своих проектов.
Я лично включил большую часть своего проекта в эту новую систему сборки и имел некоторые проблемы из-за отсутствия поддержки в некоторых конкретных ситуациях. Одна из них - поддержка для переименования полномочий ContentProvider
Новая система, созданная на базе Android, позволяет обрабатывать различные типы вашего приложения, просто изменяя имя пакета во время сборки. Одним из основных преимуществ этого улучшения является то, что теперь вы можете одновременно использовать две разные версии вашего приложения на одном устройстве. Например:
android {
compileSdkVersion 17
buildToolsVersion "17.0.0"
defaultConfig {
packageName "com.cyrilmottier.android.app"
versionCode 1
versionName "1"
minSdkVersion 14 // Listen to +Jeff Gilfelt advices :)
targetSdkVersion 17
}
buildTypes {
debug {
packageNameSuffix ".debug"
versionNameSuffix "-debug"
}
}
}
Используя такую конфигурацию Gradle, вы можете собрать два разных APK:
• Отладка APK с именем пакета com.cyrilmottier.android.app.debug • APK выпуска с именем пакета com.cyrilmottier.android.app
Единственная проблема заключается в том, что вы не сможете одновременно установить оба APK, если они оба раскрывают ContentProvider с теми же полномочиями. Довольно логически нам нужно переименовать полномочия в зависимости от текущего типа сборки... но это не поддерживается системой сборки Gradle (еще?... я уверен, что она будет исправлена в ближайшее время). Итак, вот путь:
Сначала нам нужно переместить декларацию ContentProvider поставщика Android в соответствующий тип сборки. Для этого мы просто имеем:
ЦСИ/отлаживать/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cyrilmottier.android.app"
android:versionCode="1"
android:versionName="1">
<application>
<provider
android:name=".provider.Provider1"
android:authorities="com.cyrilmottier.android.app.debug.provider"
android:exported="false" />
</application>
</manifest>
SRC/выпуск/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cyrilmottier.android.app"
android:versionCode="1"
android:versionName="1">
<application>
<provider
android:name=".provider.Provider1"
android:authorities="com.cyrilmottier.android.app.provider"
android:exported="false" />
</application>
</manifest>
Обязательно удалите объявление ContentProvider из AndroidManifest.xml в src/main/, поскольку Gradle не знает, как объединить ContentProviders с тем же именем, но с другим полномочием.
Наконец, нам может потребоваться доступ к полномочию в коде. Это можно сделать довольно легко, используя файл BuildConfig и метод buildConfig:
android {
// ...
final PROVIDER_DEBUG = "com.cyrilmottier.android.app.debug.provider"
final PROVIDER_RELEASE = "com.cyrilmottier.android.app.provider"
buildTypes {
debug {
// ...
buildConfigField "String", "PROVIDER_AUTHORITY", PROVIDER_DEBUG
}
release {
buildConfigField "String", "PROVIDER_AUTHORITY", PROVIDER_RELEASE
}
}
}
Благодаря этому обходному пути вы сможете использовать BuildConfig.PROVIDER_AUTHORITY в своем ProviderContract и одновременно устанавливать две разные версии своего приложения.
Оригинал в Google+:
https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ
Ответ 3
В то время как пример Cyril отлично работает, если у вас есть только несколько типов сборки, он быстро усложняется, если у вас есть много типов сборки и/или продуктов, поскольку вам нужно поддерживать множество разных AndroidManifest.xml.
Наш проект состоит из 3 разных типов сборки и 6 вариантов вкуса, состоящих из 18 вариантов сборки, поэтому вместо этого мы добавили поддержку ".res-auto" в органах ContentProvider, которые расширяются до текущего имени пакета и устраняют необходимость поддерживать различные AndroidManifest. xml
/**
* Version 1.1.
*
* Add support for installing multiple variants of the same app which have a
* content provider. Do this by overriding occurrences of ".res-auto" in
* android:authorities with the current package name (which should be unique)
*
* V1.0 : Initial version
* V1.1 : Support for ".res-auto" in strings added,
* eg. use "<string name="auth">.res-auto.path.to.provider</string>"
*
*/
def overrideProviderAuthority(buildVariant) {
def flavor = buildVariant.productFlavors.get(0).name
def buildType = buildVariant.buildType.name
def pathToManifest = "${buildDir}/manifests/${flavor}/${buildType}/AndroidManifest.xml"
def ns = new groovy.xml.Namespace("http://schemas.android.com/apk/res/android", "android")
def xml = new XmlParser().parse(pathToManifest)
def variantPackageName = [email protected]
// Update all content providers
xml.application.provider.each { provider ->
def newAuthorities = provider.attribute(ns.authorities).replaceAll('.res-auto', variantPackageName)
provider.attributes().put(ns.authorities, newAuthorities)
}
// Save modified AndroidManifest back into build dir
saveXML(pathToManifest, xml)
// Also make sure that all strings with ".res-auto" are expanded automagically
def pathToValues = "${buildDir}/res/all/${flavor}/${buildType}/values/values.xml"
xml = new XmlParser().parse(pathToValues)
xml.findAll{it.name() == 'string'}.each{item ->
if (!item.value().isEmpty() && item.value()[0].startsWith(".res-auto")) {
item.value()[0] = item.value()[0].replace(".res-auto", variantPackageName)
}
}
saveXML(pathToValues, xml)
}
def saveXML(pathToFile, xml) {
def writer = new FileWriter(pathToFile)
def printer = new XmlNodePrinter(new PrintWriter(writer))
printer.preserveWhitespace = true
printer.print(xml)
}
// Post processing of AndroidManifest.xml for supporting provider authorities
// across build variants.
android.applicationVariants.all { variant ->
variant.processManifest.doLast {
overrideProviderAuthority(variant)
}
}
Пример кода можно найти здесь: https://gist.github.com/cmelchior/6988275
Ответ 4
Поскольку версия плагина 0.8.3 (фактически 0.8.1, но она не работает должным образом), вы можете определить ресурсы в файле сборки, чтобы это могло быть более чистым решением, поскольку вам не нужно создавать файлы строк и дополнительные папки отладки/выпуска.
build.gradle
android {
buildTypes {
debug{
resValue "string", "authority", "com.yourpackage.debug.provider"
}
release {
resValue "string", "authority", "com.yourpackage.provider"
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourpackage"
android:versionCode="1"
android:versionName="1">
<application>
<provider
android:name=".provider.Provider1"
android:authorities="@string/authority"
android:exported="false" />
</application>
</manifest>
Ответ 5
Я не знаю, упоминает ли кто-нибудь это. Фактически после плагина android gradle 0.10+ манифестное слияние обеспечит официальную поддержку этой функции:
http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger
В AndroidManifest.xml вы можете использовать ${packageName} следующим образом:
<provider
android:name=".provider.DatabasesProvider"
android:authorities="${packageName}.databasesprovider"
android:exported="true"
android:multiprocess="true" />
И в вашем build.gradle вы можете:
productFlavors {
free {
packageName "org.pkg1"
}
pro {
packageName "org.pkg2"
}
}
См. полный пример здесь:
https://code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152
и здесь:
https://code.google.com/p/anymemo/source/browse/build.gradle#41
Ответ 6
Используйте ${applicationId}
заполнители в xml и BuildConfig.APPLICATION_ID
в коде.
Вам нужно будет расширить конструкцию script, чтобы включить заполнители в файлы xml, отличные от манифеста. Вы можете использовать исходный каталог для каждого варианта сборки для предоставления различных версий xml файлов, но обслуживание будет очень громоздким очень быстро.
AndroidManifest.xml
Вы можете использовать заполнитель приложения из ящика в манифесте. Объявите своего провайдера следующим образом:
<provider
android:name=".provider.DatabaseProvider"
android:authorities="${applicationId}.DatabaseProvider"
android:exported="false" />
Обратите внимание на бит ${applicationId}
. Это заменяется во время сборки фактическим приложением для встроенного варианта сборки.
В коде
Ваш ContentProvider должен построить строку полномочий в коде. Он может использовать класс BuildConfig.
public class DatabaseContract {
/** The authority for the database provider */
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".DatabaseProvider";
// ...
}
Обратите внимание на бит BuildConfig.APPLICATION_ID
. Это сгенерированный класс с фактическим applicationId для построенного варианта сборки.
res/xml/files, например. syncadapter.xml, accountauthenticator.xml
Если вы хотите использовать адаптер синхронизации, вам нужно будет предоставить метаданные для ContentProvider и AccountManager в xml файлах в каталоге res/xml/. Заполнитель applicationId здесь не поддерживается. Но вы можете расширить сборку script самостоятельно, чтобы взломать ее.
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="${applicationId}"
android:allowParallelSyncs="false"
android:contentAuthority="${applicationId}.DatabaseProvider"
android:isAlwaysSyncable="true"
android:supportsUploading="true"
android:userVisible="true" />
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="${applicationId}"
android:icon="@drawable/ic_launcher"
android:label="@string/account_authenticator_label"
android:smallIcon="@drawable/ic_launcher" />
Снова обратите внимание на ${applicationId}
. Это работает, только если вы добавите ниже gradle script в корень вашего модуля и примените его из build.gradle.
build.gradle
Примените дополнительную сборку script к модулю build.gradle script. Хорошее место находится под плагином Android gradle.
apply plugin: 'com.android.application'
apply from: './build-processApplicationId.gradle'
android {
compileSdkVersion 21
// etc.
встроенный processApplicationId.gradle
Ниже приведен рабочий источник для сборки res/xml/placeholder script. Более качественная документация доступна на github. Усовершенствования и расширения приветствуются.
def replace(File file, String target, String replacement) {
def result = false;
def reader = new FileReader(file)
def lines = reader.readLines()
reader.close()
def writer = new FileWriter(file)
lines.each { line ->
String replacedLine = line.replace(target, replacement)
writer.write(replacedLine)
writer.write("\n")
result = result || !replacedLine.equals(line)
}
writer.close()
return result
}
def processXmlFile(File file, String applicationId) {
if (replace(file, "\${applicationId}", applicationId)) {
logger.info("Processed \${applicationId} in $file")
}
}
def processXmlDir(File dir, String applicationId) {
dir.list().each { entry ->
File file = new File(dir, entry)
if (file.isFile()) {
processXmlFile(file, applicationId)
}
}
}
android.applicationVariants.all { variant ->
variant.mergeResources.doLast {
def applicationId = variant.mergedFlavor.applicationId + (variant.buildType.applicationIdSuffix == null ? "" : variant.buildType.applicationIdSuffix)
def path = "${buildDir}/intermediates/res/${variant.dirName}/xml/"
processXmlDir(new File(path), applicationId)
}
}
strings.xml
По моему мнению, нет необходимости добавлять поддержку заполнителя для строк ресурсов. Для вышеуказанного варианта использования, по крайней мере, он не нужен. Однако вы можете легко изменить script, чтобы не только заменить заполнители в каталоге res/xml/, но также и в каталоге res/values /.
Ответ 7
Я предпочел бы смесь между Кирилом и rciovati. Я думаю, что это проще, у вас есть только две модификации.
build.gradle
выглядит так:
android {
...
productFlavors {
production {
packageName "package.name.production"
resValue "string", "authority", "package.name.production.provider"
buildConfigField "String", "AUTHORITY", "package.name.production.provider"
}
testing {
packageName "package.name.debug"
resValue "string", "authority", "package.name.debug.provider"
buildConfigField "String", "AUTHORITY", "package.name.debug.provider"
}
}
...
}
И AndroidManifest.xml
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="package.name" >
<application
...>
<provider android:name=".contentprovider.Provider" android:authorities="@string/authority" />
</application>
</manifest>
Ответ 8
Основываясь на образце @ChristianMelchior, здесь мое решение, которое исправляет две проблемы в предыдущих решениях:
-
решения, которые меняют values.xml в каталоге сборки, приводят к полному переустановке ресурсов (включая адаптацию всех чертежей)
-
по неизвестной причине IntelliJ (и, возможно, Android Studio) не надежно обрабатывает ресурсы, в результате чего сборка содержит не замененные .res-auto
полномочия поставщика
Это новое решение делает вещи больше Gradle путем создания новой задачи и позволяет создавать инкрементные сборки путем определения входных и выходных файлов.
-
создайте файл (в примере я поместил его в каталог variants
), отформатированный как XML файл ресурса, который содержит строковые ресурсы. Они будут объединены в ресурсы приложения, и любое значение .res-auto
в значениях будет заменено на имя варианта варианта, например <string name="search_provider">.res-auto.MySearchProvider</string>
-
добавить build_extras.gradle
файл из этот смысл в ваш проект и ссылаться на него с основного build.gradle
, добавив apply from: './build_extras.gradle'
где-то над блоком android
-
убедитесь, что вы установили имя пакета по умолчанию, добавив его в блок android.defaultConfig
build.gradle
-
в AndroidManifest.xml
и других файлах конфигурации (например, xml/searchable.xml
для поставщиков автозавершения поиска), обратитесь к провайдеру (например, @string/search_provider
)
-
если вам нужно получить то же имя, вы можете использовать переменную BuildConfig.PACKAGE_NAME
, например BuildConfig.PACKAGE_NAME + ".MySearchProvider"
https://gist.github.com/paour/9189462
<ч/" > Обновление: этот метод работает только на Android 2.2.1 и более поздних версиях. Для более ранних платформ см. этот ответ, который имеет свой собственный набор проблем, поскольку новое слияние манифеста все еще очень грубо по краям...
Ответ 9
gradle.build
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.example.awsomeapp"
minSdkVersion 9
targetSdkVersion 23
versionCode 1
versionName "1.0.0"
}
productFlavors
{
prod {
applicationId = "com.example.awsomeapp"
}
demo {
applicationId = "com.example.awsomeapp.demo"
versionName = defaultConfig.versionName + ".DEMO"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
applicationIdSuffix ".debug"
versionNameSuffix = ".DEBUG"
debuggable true
}
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
// rename the apk
def file = output.outputFile;
def newName;
newName = file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk");
newName = newName.replace(project.name, "awsomeapp");
output.outputFile = new File(file.parent, newName);
}
//Generate values Content Authority and Account Type used in Sync Adapter, Content Provider, Authenticator
def valueAccountType = applicationId + '.account'
def valueContentAuthority = applicationId + '.authority'
//generate fields in Resource string file generated.xml
resValue "string", "content_authority", valueContentAuthority
resValue "string", "account_type", valueAccountType
//generate fields in BuildConfig class
buildConfigField "String", "ACCOUNT_TYPE", '"'+valueAccountType+'"'
buildConfigField "String", "CONTENT_AUTHORITY", '"'+valueContentAuthority+'"'
//replace field ${valueContentAuthority} in AndroidManifest.xml
mergedFlavor.manifestPlaceholders = [ valueContentAuthority: valueContentAuthority ]
}
}
authenticator.xml
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/account_type"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:smallIcon="@drawable/ic_launcher" />
sync_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="@string/content_authority"
android:accountType="@string/account_type"
android:userVisible="true"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
android:supportsUploading="true"/>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0.0" package="com.example.awsomeapp">
<uses-permission android:name="android.permission.GET_ACCOUNTS"/><!-- SyncAdapter and GCM requires a Google account. -->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<!-- GCM Creates a custom permission so only this app can receive its messages. -->
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
<application....
.......
<!-- Stub Authenticator -->
<service
android:name="com.example.awsomeapp.service.authenticator.CAuthenticatorService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator"/>
</service>
<!-- -->
<!-- Sync Adapter -->
<service
android:name="com.example.awsomeapp.service.sync.CSyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/sync_adapter" />
</service>
<!-- -->
<!-- Content Provider -->
<provider android:authorities="${valueContentAuthority}"
android:exported="false"
android:name="com.example.awsomeapp.database.contentprovider.CProvider">
</provider>
<!-- -->
</application>
</manifest>
код:
public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY;
public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
Ответ 10
Я написал blogpost с образцом проекта Github, который решает эту проблему (и другие подобные проблемы) несколько иначе, чем Cyril's.
http://brad-android.blogspot.com/2013/08/android-gradle-building-unique-build.html
Ответ 11
К сожалению, текущая версия (0.4.1) плагина android, похоже, не является хорошим решением для этого. У меня еще не было времени попробовать это, но возможным решением этой проблемы было бы использовать строковый ресурс @string/provider_authority
и использовать его в манифесте: android:authority="@string/provider_authority"
. Затем у вас есть res/values/provider.xml
в папке res каждого типа сборки, который должен переопределить полномочия, в вашем случае это будет src/debug/res
Я искал создание xml файла на лету, но опять же, похоже, в текущей версии плагина нет хороших перехватчиков. Я бы порекомендовал вставить запрос на функцию, но я могу представить, что в эту же проблему попадет больше людей.
Ответ 12
Ответ на этот пост работает для меня.
http://www.kevinrschultz.com/blog/2014/03/23/using-android-content-providers-with-multiple-package-names/
Я использую 3 разных вкуса, поэтому я создаю 3 манифеста с контент-провайдером в каждом вкусе, поскольку kevinrschultz сказал:
productFlavors {
free {
packageName "your.package.name.free"
}
paid {
packageName "your.package.name.paid"
}
other {
packageName "your.package.name.other"
}
}
Ваш основной манифест не включает поставщиков:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Permissions -->
<application>
<!-- Nothing about Content Providers at all -->
<!-- Activities -->
...
<!-- Services -->
...
</application>
И ваш манифест в каждом вашем вкусе, включая поставщика.
Free:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<application>
<!-- Content Providers -->
<provider
android:name="your.package.name.Provider"
android:authorities="your.package.name.free"
android:exported="false" >
</provider>
</application>
</manifest>
Выплачено:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<application>
<!-- Content Providers -->
<provider
android:name="your.package.name.Provider"
android:authorities="your.package.name.paid"
android:exported="false" >
</provider>
</application>
</manifest>
Другое:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
<application>
<!-- Content Providers -->
<provider
android:name="your.package.name.Provider"
android:authorities="your.package.name.other"
android:exported="false" >
</provider>
</application>
</manifest>
Ответ 13
Почему бы просто не добавить это?
type.packageNameSuffix = ". $type.name"
Ответ 14
Мое решение - использовать замену заполнителя в AndroidManifest.xml
. Он также обрабатывает атрибуты packageNameSuffix
, поэтому вы можете иметь debug
и release
, а также любые другие пользовательские сборки на одном устройстве.
applicationVariants.all { variant ->
def flavor = variant.productFlavors.get(0)
def buildType = variant.buildType
variant.processManifest.doLast {
println '################# Adding Package Names to Manifest #######################'
replaceInManifest(variant,
'PACKAGE_NAME',
[flavor.packageName, buildType.packageNameSuffix].findAll().join()) // ignores null
}
}
def replaceInManifest(variant, fromString, toString) {
def flavor = variant.productFlavors.get(0)
def buildtype = variant.buildType
def manifestFile = "$buildDir/manifests/${flavor.name}/${buildtype.name}/AndroidManifest.xml"
def updatedContent = new File(manifestFile).getText('UTF-8').replaceAll(fromString, toString)
new File(manifestFile).write(updatedContent, 'UTF-8')
}
У меня есть на gist
, если вы хотите узнать, развивается ли он позже.
Я нашел более элегантный подход, чем несколько ресурсов и подходы к анализу XML.