it-swarm.com.de

Verwenden von Build-Typen in Gradle zum Ausführen derselben App, die ContentProvider auf einem Gerät verwendet

Ich habe Gradle so eingerichtet, dass der Debug-App ein Paketnamensuffix hinzugefügt wird, damit ich die verwendete Release-Version und die Debug-Version auf einem Telefon verwenden kann. Ich habe auf Folgendes verwiesen: http://tools.Android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types

Meine build.gradle-Datei sieht folgendermaßen aus:

...
Android
{
    ...
    buildTypes
    {
        debug
        {
            packageNameSuffix ".debug"
            versionNameSuffix " debug"
        }
    }
}

Alles funktioniert einwandfrei, bis ich mit der Verwendung eines ContentProviders in meiner App beginne. Ich bekomme:

Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]

Ich verstehe, dass dies passiert, weil zwei Apps (Release und Debug) dieselbe ContentProvider-Berechtigung registrieren.

Ich sehe eine Möglichkeit, dies zu lösen. Wenn ich das richtig verstehe, sollten Sie in der Lage sein, verschiedene Dateien anzugeben, die beim Erstellen verwendet werden sollen. Dann sollte es mir möglich sein, verschiedene Berechtigungen in verschiedene Ressourcendateien zu setzen (und von Manifest aus Berechtigungen als Zeichenkettenressource festzulegen) und Gradle anzuweisen, verschiedene Ressourcen für die Debug-Erstellung zu verwenden. Ist das möglich? Wenn ja, dann wäre ein Hinweis darauf, wie dies erreicht werden kann, großartig!

Oder ist es möglich, Manifest mit Gradle direkt zu ändern? Jede andere Lösung, wie dieselbe App mit ContentProvider auf einem Gerät ausgeführt werden kann, ist immer willkommen.

121
MantasV

Keine der vorhandenen Antworten stellte mich zufrieden, jedoch war Liberty nah. So mache ich es also. Zunächst arbeite ich im Moment mit:

  • Android Studio Beta 0.8.2
  • Gradle Plugin 0.12. +
  • Gradle 1.12

Mein Ziel ist es, Debug Version zusammen mit Release Version auf demselben Gerät unter Verwendung desselben ContentProvider auszuführen.


In build.gradle Ihres App-Set-Suffixes für den Debug-Build:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

In AndroidManifest.xml Datei setze Android:authorities Eigenschaft deines ContentProvider:

<provider
    Android:name="com.example.app.YourProvider"
    Android:authorities="${applicationId}.provider"
    Android:enabled="true"
    Android:exported="false" >
</provider>

In Ihrer Code set AUTHORITY -Eigenschaft, die bei Bedarf in Ihrer Implementierung verwendet werden kann:

public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";

Tipp: Vorher war es BuildConfig.PACKAGE_NAME

Das ist es! Es wird wie ein Zauber wirken. Lesen Sie weiter, wenn Sie SyncAdapter verwenden!


Update für SyncAdapter (14.11.2014)

Ich werde noch einmal mit meinem aktuellen Setup beginnen:

  • Android Studio Beta 0.9.2
  • Gradle Plugin 0.14.1
  • Gradle 2.1

Grundsätzlich können Sie einige Werte für verschiedene Builds über die Datei build.gradle anpassen:

  • verwenden Sie buildConfigField , um über die Klasse BuildConfig.Java darauf zuzugreifen
  • verwenden Sie resValue , um von Ressourcen, z. @ string/your_value

Als Alternative zu Ressourcen können Sie separate buildType- oder flavor-Verzeichnisse erstellen und XMLs oder Werte in diesen überschreiben. Im folgenden Beispiel werde ich es jedoch nicht verwenden.

Beispiel


Fügen Sie in der Datei build.gradle Folgendes hinzu:

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"'
    }
}

Sie sehen die Ergebnisse in der Klasse BuildConfig.Java

public static final String ACCOUNT_TYPE = "your.syncadapter.type.debug";

und in build/generated/res/generated/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>

Verwenden Sie in Ihrer Datei authenticator.xml die in build.gradle angegebene Ressource

<?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"
/>

Verwenden Sie in Ihrer syncadapter.xml dieselbe Ressource erneut und @ string/authority auch

<?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"
        />

Tipp: Die automatische Vervollständigung (Strg + Leertaste) funktioniert für diese generierten Ressourcen nicht, sodass Sie sie manuell eingeben müssen

221
Damian Petla

Neuer Android Build-System-Tipp: Umbenennen der ContentProvider-Berechtigung

Ich vermute, Sie alle haben von dem neuen Gradle-basierten Build-System Android gehört. Seien wir ehrlich, dieses neue Build-System ist im Vergleich zum vorherigen ein großer Fortschritt. Es ist noch nicht endgültig (zum Zeitpunkt des Schreibens ist die neueste Version 0.4.2), aber Sie können es bereits sicher in den meisten Ihrer Projekte verwenden.

Ich habe den größten Teil meines Projekts persönlich auf dieses neue Build-System umgestellt und hatte einige Probleme, weil ich in bestimmten Situationen keinen Support hatte. Eine davon ist die Unterstützung für das Umbenennen von ContentProvider-Berechtigungen

Mit dem neuen, von Android erstellten System können Sie mit verschiedenen Arten Ihrer App umgehen, indem Sie einfach den Paketnamen zum Zeitpunkt der Erstellung ändern. Einer der Hauptvorteile dieser Verbesserung ist, dass Sie jetzt zwei verschiedene Versionen Ihrer App gleichzeitig auf demselben Gerät installieren können. Zum Beispiel:

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"
       }
   }
}

Mit einer solchen Gradle-Konfiguration können Sie zwei verschiedene APKs zusammenstellen:

• Ein Debug-APK mit dem Paketnamen com.cyrilmottier.Android.app.debug. • Ein Release-APK mit dem Paketnamen com.cyrilmottier.Android.app

Das einzige Problem dabei ist, dass Sie die beiden APKs nicht gleichzeitig installieren können, wenn beide einen ContentProvider mit denselben Berechtigungen bereitstellen. Ziemlich logisch ist, dass wir die Berechtigung abhängig vom aktuellen Build-Typ umbenennen müssen… aber dies wird vom Gradle-Build-System nicht unterstützt (noch nicht? ... Ich bin sicher, dass es bald behoben wird). Also hier ist ein Weg zu gehen:

Zuerst müssen wir die ContentProvider-Deklaration des Providers Android in den entsprechenden Build-Typ verschieben. Dazu haben wir einfach:

src/debug/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/release/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>

Stellen Sie sicher, dass Sie die ContentProvider-Deklaration aus der AndroidManifest.xml-Datei in src/main/entfernen, da Gradle nicht weiß, wie ContentProvider mit demselben Namen, aber einer anderen Berechtigung zusammengeführt werden.

Schließlich müssen wir möglicherweise auf die Berechtigung im Code zugreifen. Dies kann ziemlich einfach mit der BuildConfig-Datei und der buildConfig-Methode durchgeführt werden:

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
       }
   }
}

Dank dieser Problemumgehung können Sie BuildConfig.PROVIDER_AUTHORITY in Ihrem ProviderContract verwenden und gleichzeitig zwei verschiedene Versionen Ihrer App installieren.


Ursprünglich auf Google+: https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ

39
Cyril Mottier

Während Cyrils Beispiel hervorragend funktioniert, wenn Sie nur wenige Build-Typen haben, wird es schnell kompliziert, wenn Sie viele Build-Typen und/oder Produktvarianten haben, da Sie viele verschiedene AndroidManifest.xmls verwalten müssen.

Unser Projekt besteht aus 3 verschiedenen Build-Typen und 6 Varianten mit insgesamt 18 Build-Varianten. Stattdessen haben wir in den ContentProvider-Berechtigungen die Unterstützung für ".res-auto" hinzugefügt, die auf den aktuellen Paketnamen erweitert und die Pflege unterschiedlicher AndroidManifest.xml-Dateien überflüssig macht

/**
 * 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)
    }
}

Beispielcode finden Sie hier: https://Gist.github.com/cmelchior/6988275

23

Seit der Plugin-Version 0.8.3 (tatsächlich 0.8.1, aber es funktionierte nicht richtig) können Sie Ressourcen in der Build-Datei definieren, sodass dies eine sauberere Lösung sein kann, da Sie keine Strings-Dateien erstellen oder zusätzliche Debugs/Releases durchführen müssen Ordner.

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>
20
rciovati

Ich weiß nicht, ob es jemand erwähnt. Nach Android gradle plugin 0.10+ wird die manifestierte Fusion die offizielle Unterstützung für diese Funktion bereitstellen: http://tools.Android.com/tech-docs/new-build -system/user-guide/manifest-merger

In AndroidManifest.xml können Sie $ {packageName} folgendermaßen verwenden:

<provider
    Android:name=".provider.DatabasesProvider"
    Android:authorities="${packageName}.databasesprovider"
    Android:exported="true"
    Android:multiprocess="true" />

Und in Ihrem build.gradle können Sie haben:

productFlavors {
    free {
        packageName "org.pkg1"
    }
    pro {
        packageName "org.pkg2"
    }
}

Ein vollständiges Beispiel finden Sie hier: https://code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152

und hier: https://code.google.com/p/anymemo/source/browse/build.gradle#41

13
Liberty

Verwenden ${applicationId} Platzhalter in xml und BuildConfig.APPLICATION_ID in Code.

Sie müssen das Erstellungsskript erweitern, um Platzhalter in anderen XML-Dateien als dem Manifest zu aktivieren. Sie könnten ein Quellverzeichnis pro Build-Variante verwenden, um verschiedene Versionen der XML-Dateien bereitzustellen, aber die Wartung wird sehr schnell umständlich.

AndroidManifest.xml

Sie können den Platzhalter applicationId direkt im Manifest verwenden. Deklarieren Sie Ihren Provider folgendermaßen:

<provider
    Android:name=".provider.DatabaseProvider"
    Android:authorities="${applicationId}.DatabaseProvider"
    Android:exported="false" />

Beachten Sie das ${applicationId} Bit. Dies wird zur Erstellungszeit durch die tatsächliche applicationId für die Erstellungsvariante ersetzt, die erstellt wird.

Im Code

Ihr ContentProvider muss die Berechtigungszeichenfolge im Code erstellen. Es kann die BuildConfig-Klasse verwenden.

public class DatabaseContract {
    /** The authority for the database provider */
    public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".DatabaseProvider";
    // ...
}

Beachten Sie das BuildConfig.APPLICATION_ID Bit. Es ist eine generierte Klasse mit der tatsächlichen applicationId für die Build-Variante, die erstellt wird.

res/xml/files, z.B. syncadapter.xml, accountauthenticator.xml

Wenn Sie einen Synchronisationsadapter verwenden möchten, müssen Sie Metadaten für den ContentProvider und den AccountManager in XML-Dateien im Verzeichnis res/xml/bereitstellen. Der Platzhalter applicationId wird hier nicht unterstützt. Sie können das Build-Skript aber auch selbst erweitern, um es zu hacken.

<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" />

Beachten Sie auch hier das ${applicationId}. Dies funktioniert nur, wenn Sie das folgende gradle-Skript zum Stammverzeichnis Ihres Moduls hinzufügen und es aus build.gradle anwenden.

build.gradle

Wenden Sie das zusätzliche Erstellungsskript aus dem Modul build.gradle an. Ein guter Platz ist unterhalb des Android gradle plugins.

apply plugin: 'com.Android.application'
apply from: './build-processApplicationId.gradle'

Android {
    compileSdkVersion 21
    // etc.

build-processApplicationId.gradle

Unten finden Sie eine funktionierende Quelle für ein Erstellungsskript für res/xml/placeholder. Eine besser dokumentierte Version ist verfügbar unter github . Verbesserungen und Erweiterungen sind willkommen.

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

Meiner Meinung nach müssen keine Platzhalter für Ressourcenzeichenfolgen hinzugefügt werden. Zumindest für den oben genannten Anwendungsfall wird es nicht benötigt. Sie können das Skript jedoch problemlos so ändern, dass Platzhalter nicht nur im Verzeichnis res/xml /, sondern auch im Verzeichnis res/values ​​/ ersetzt werden.

8
Rob Meeuwisse

Ich würde eher eine Mischung zwischen Cyril und rciovati bevorzugen. Ich denke ist einfacher, du hast nur zwei Modifikationen.

Der build.gradle Sieht so aus:

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"
        }
    }
    ...
}

Und der 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>
6
icastell

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>

Code:

public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY;
public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
5
maros136

Ich habe einen Blogpost mit Github-Beispielprojekt geschrieben, der dieses Problem (und andere ähnliche Probleme) auf eine etwas andere Art und Weise als Cyrils angeht.

http://brad-Android.blogspot.com/2013/08/Android-gradle-building-unique-build.html

3
icecreamman

Leider scheint die aktuelle Version (0.4.1) des Android= Plugins keine gute Lösung dafür zu sein. Ich hatte noch keine Zeit, dies zu versuchen, aber eine mögliche Problemumgehung Für dieses Problem müssten Sie eine Zeichenfolgenressource @string/provider_authority verwenden und diese im Manifest verwenden: Android:authority="@string/provider_authority". Sie haben dann ein res/values/provider.xml im res-Ordner jedes Build-Typs, der sollte die Autorität überschreiben, in Ihrem Fall wäre dies src/debug/res

Ich habe versucht, die XML-Datei im laufenden Betrieb zu generieren, aber auch hier scheint es in der aktuellen Version des Plugins keine guten Haken dafür zu geben. Ich würde jedoch empfehlen, eine Feature-Anfrage einzureichen. Ich kann mir vorstellen, dass mehr Leute auf dasselbe Problem stoßen werden.

Die Antwort in diesem Beitrag funktioniert für mich.

http://www.kevinrschultz.com/blog/2014/03/23/using-Android-content-providers-with-multiple-package-names/

Ich benutze 3 verschiedene Geschmacksrichtungen, also erstelle ich 3 Manifeste mit Inhaltsanbietern in jeder Geschmacksrichtung, wie kevinrschultz sagte:

productFlavors {
    free {
        packageName "your.package.name.free"
    }

    paid {
        packageName "your.package.name.paid"
    }

    other {
        packageName "your.package.name.other"
    }
}

Ihr Hauptmanifest enthält keine Anbieter:

<?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>

Und Ihr Manifest in jedem Geschmack einschließlich Anbieter.

Frei:

<?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>

Bezahlt:

<?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>

Andere:

<?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>
2
jcmore2

Meine Lösung ist die Verwendung der Platzhalterersetzung in AndroidManifest.xml. Es verarbeitet auch packageNameSuffix Attribute, sodass Sie debug und release sowie andere benutzerdefinierte Builds auf demselben Gerät haben können.

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')
}

Ich habe es auf einem Gist auch, wenn Sie sehen möchten, ob es sich später entwickelt.

Ich stellte fest, dass dieser Ansatz eleganter ist als die Ansätze mit mehreren Ressourcen und XML-Parsing.

0
Saad Farooq

Warum nicht einfach hinzufügen?

type.packageNameSuffix = ". $ type.name"

0
guydemossyrock