Использование типов сборки в 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;

Ответ 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.