Build-typen gebruiken in Gradle om dezelfde app uit te voeren die ContentProvider op één apparaat gebruikt

Ik heb Gradle ingesteld om het achtervoegsel van de pakketnaam toe te voegen aan mijn debug-app, zodat ik een releaseversie heb die ik gebruik en debug-versie op één telefoon. Ik verwees naar dit: http://tools.android.com /tech-docs/new-build-system/user-guide#TOC-Build-Types

Mijn build.gradle-bestand ziet er als volgt uit:

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

Alles werkt prima totdat ik een ContentProvider in mijn app ga gebruiken. Ik krijg:

Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]

Ik begrijp dat dit gebeurt omdat twee apps (release en debug) dezelfde ContentProvider-autoriteit registreren.

Ik zie een mogelijkheid om dit op te lossen. Als ik het goed begrijp, zou je verschillende bestanden moeten kunnen specificeren om te gebruiken bij het bouwen. Dan zou ik in staat moeten zijn om verschillende autoriteiten in verschillende bronbestanden te plaatsen (en vanuit Manifest autoriteit als tekenreeksbron in te stellen) en Gradle vertellen om verschillende bronnen te gebruiken voor het maken van foutopsporing. Is dat mogelijk? Zo ja, dan zouden alle tips om dat te bereiken geweldig zijn!

Of is het misschien mogelijk om Manifest rechtstreeks aan te passen met Gradle? Elke andere oplossing voor het uitvoeren van dezelfde app met ContentProvider op één apparaat is altijd welkom.


Antwoord 1, autoriteit 100%

Geen van de bestaande antwoorden bevredigde me, maar Liberty was dichtbij. Dit is dus hoe ik het doe.
Allereerst werk ik momenteel samen met:

  • Android Studio Beta 0.8.2
  • Gradle-plug-in 0.12.+
  • Gradel 1.12

Mijn doelis om de debug-versie samen met de release-versie op hetzelfde apparaat uit te voeren met dezelfde ContentProvider.


In build.gradlevan het achtervoegsel van uw app-set voor Debug-build:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

In AndroidManifest.xmlbestand stel android:authoritieseigenschap van uw ContentProviderin:

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

Stel in uw codede eigenschap AUTHORITYin die overal in uw implementatie kan worden gebruikt:

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

Tip:Voordat het BuildConfig.PACKAGE_NAME

was

Dat is het! Het zal werken als een charme. Blijf lezen als u SyncAdapter gebruikt!


Update voor SyncAdapter (14.11.2014)

Ik zal opnieuw beginnen met mijn huidige setup:

  • Android Studio Beta 0.9.2
  • Gradle-plug-in 0.14.1
  • Gradel 2.1

Kortom, als u enkele waarden voor verschillende builds moet aanpassen, kunt u dit doen vanuit het build.gradle-bestand:

  • gebruik buildConfigFieldom toegang te krijgen vanuit de klasse BuildConfig.java
  • gebruik resValueom er toegang toe te krijgen vanuit bronnen, b.v. @string/your_value

Als alternatief voor bronnen kunt u afzonderlijke buildType- of smaakmappen maken en XML’s of waarden daarin overschrijven. Ik ga het echter niet gebruiken in het onderstaande voorbeeld.

Voorbeeld


Voeg in het bestand build.gradlehet volgende toe:

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

U ziet resultaten in buildconfig.java klasse

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

en in Build / gegenereerd / res / gegenereerd / debug / waarden / gegenereerd.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>

In uw authenticator.xml Gebruik resource opgegeven in build.gradle-bestand

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

In uw syncadapter.xml Gebruik dezelfde resource opnieuw en @ string / autoriteiten ook

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

TIP: Autocompletion (Ctrl + Space) werkt niet voor deze gegenereerde bron, dus u moet ze handmatig typen


Antwoord 2, Autoriteit 17%

Nieuwe Android Build System Tip: Content Wedstrijdwedstrijd Reneming

Ik denk dat jullie allemaal van het nieuwe Android Gradle-gebaseerde build-systeem hebben gehoord. Laten we eerlijk zijn, dit nieuwe build-systeem is een enorme stap voorwaarts in vergelijking met de vorige. Het is nog geen definitief (vanaf dit schrijven, de nieuwste versie is 0,4.2), maar u kunt het al veilig in de meeste van uw projecten gebruiken.

Ik heb het grootste deel van mijn project in dit nieuwe build-systeem geschakeld en had wat problemen vanwege het gebrek aan ondersteuning in sommige specifieke situaties. Daarvan is er de ondersteuning voor Content Provider Authority Renaming

Met het nieuwe Android-gebouwd systeem kunt u met verschillende typen van uw app omgaan door de pakketnaam op de buildtijd te wijzigen. Een van het grootste voordeel van deze verbetering is dat u nu tegelijkertijd twee verschillende versies van uw app op hetzelfde apparaat hebt geïnstalleerd. Bijvoorbeeld:

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

Met behulp van een dergelijke gradle-configuratie kunt u twee verschillende APKS samenstellen:

• een debug-apk met de com.cyrilmottier.android.app.debug pakketnaam
• Een release APK met de Com.cyrilmottier.Android.App-pakketnaam

Het enige probleem daarmee is dat je de twee APK’s niet tegelijkertijd kunt installeren als ze allebei een ContentProvider met dezelfde autoriteiten blootstellen. Vrij logisch dat we de autoriteit moeten hernoemen, afhankelijk van het huidige buildtype … maar dit wordt niet ondersteund door het Gradle-buildsysteem (nog? … Ik weet zeker dat het snel zal worden opgelost). Dus hier is een manier om te gaan:

Eerst moeten we het Android-manifest ContentProvider-declaratie van de provider verplaatsen naar het juiste buildtype. Om dat te doen hebben we gewoon:

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>

Zorg ervoor dat u de ContentProvider-declaratie verwijdert uit de AndroidManifest.xml in src/main/ omdat Gradle niet weet hoe ContentProviders met dezelfde naam maar een andere autoriteit moeten worden samengevoegd.

Ten slotte moeten we mogelijk toegang krijgen tot de autoriteit in de code. Dit kan vrij eenvoudig worden gedaan met behulp van het BuildConfig-bestand en de buildConfig-methode:

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

Dankzij deze tijdelijke oplossing kunt u BuildConfig.PROVIDER_AUTHORITY in uw ProviderContract gebruiken en tegelijkertijd twee verschillende versies van uw app installeren.


Oorspronkelijk op Google+:
https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ


Antwoord 3, autoriteit 10%

Hoewel het voorbeeld van Cyril prima werkt als je maar een paar buildtypes hebt, wordt het al snel ingewikkeld als je veel buildtypes en/of productaroma’s hebt, omdat je veel verschillende AndroidManifest.xml’s moet onderhouden.

Ons project bestaat uit 3 verschillende buildtypes en 6 smaken met in totaal 18 buildvarianten, dus in plaats daarvan hebben we ondersteuning toegevoegd voor “.res-auto” in ContentProvider-autoriteiten, die uitbreidt naar de huidige pakketnaam en de noodzaak wegneemt om verschillende AndroidManifest te onderhouden. 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 = xml.@package
    // 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)
    }
}

Voorbeeldcode is hier te vinden: https://gist.github.com/cmelchior/6988275


Antwoord 4, autoriteit 8%

Sinds de plug-in versie 0.8.3 (eigenlijk 0.8.1 maar het werkte niet goed) kun je bronnen definiëren in het build-bestand, dus dit zou een schonere oplossing kunnen zijn, omdat je geen strings-bestanden of aanvullende debug/release mappen.

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>

Antwoord 5, autoriteit 6%

Ik weet niet of iemand het vermeldt. Eigenlijk na Android gradle plugin 0.10+, zal de manifest-fusie de officiële ondersteuning voor deze functie bieden:
http://tools.android.com/tech-docs/new- build-system/gebruikershandleiding/manifest-merger

In AndroidManifest.xml kunt u ${packageName} als volgt gebruiken:

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

En in je build.gradle kun je het volgende hebben:

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

Bekijk hier het volledige voorbeeld:
https://code.google.com/p/anymemo/source/browse/AndroidManifest. xml#152

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


Antwoord 6, autoriteit 3%

Gebruik ${applicationId}tijdelijke aanduidingen in xml en BuildConfig.APPLICATION_IDin code.

U moet het buildscript uitbreiden om tijdelijke aanduidingen in andere xml-bestanden dan het manifest in te schakelen. U kunt per buildvariant een brondirectory gebruiken om verschillende versies van de xml-bestanden aan te bieden, maar het onderhoud wordt al snel omslachtig.

AndroidManifest.xml

U kunt de tijdelijke aanduiding applicationId gebruiken in het manifest. Verklaar uw provider als volgt:

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

Let op het ${applicationId}bit. Dit wordt tijdens het bouwen vervangen door de daadwerkelijke applicationId voor de buildvariant die wordt gebouwd.

In code

Uw ContentProvider moet de autoriteitstekenreeks in code construeren. Het kan de klasse BuildConfig gebruiken.

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

Let op het BuildConfig.APPLICATION_IDbit. Het is een gegenereerde klasse met de daadwerkelijke applicationId voor de buildvariant die wordt gebouwd.

res/xml/-bestanden, bijv. syncadapter.xml, accountauthenticator.xml

Als u een Sync Adapter wilt gebruiken, moet u metagegevens voor de ContentProvider en AccountManager in xml-bestanden in de res/xml/-directory aanleveren. De tijdelijke aanduiding applicationId wordt hier niet ondersteund. Maar je kunt het buildscript zelf uitbreiden om het te 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" />

Nogmaals, let op de ${applicationId}. Dit werkt alleen als je het onderstaande gradle-script toevoegt aan de root van je module en het toepast vanuit build.gradle.

build.gradle

Pas het extra buildscript van het module build.gradle-script toe. Een goede plaats is onder de Android-gradle-plug-in.

apply plugin: 'com.android.application'
apply from: './build-processApplicationId.gradle'
android {
    compileSdkVersion 21
    // etc.

build-processApplicationId.gradle

Hieronder staat een werkende bron voor een res/xml/placeholder build-script. Een beter gedocumenteerde versie is beschikbaar op github. Verbeteringen en uitbreidingen zijn welkom.

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

Naar mijn mening is het niet nodig om tijdelijke ondersteuning voor resourcestrings toe te voegen. Voor de bovenstaande use case is het in ieder geval niet nodig. U kunt het script echter gemakkelijk wijzigen om niet alleen tijdelijke aanduidingen in de map res/xml/ te vervangen, maar ook in de map res/values/.


Antwoord 7, autoriteit 3%

Ik heb liever een mix tussen Cyril en rciovati. Ik denk dat het eenvoudiger is, je hebt maar twee aanpassingen.

De build.gradleziet er als volgt uit:

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

En de 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>

Antwoord 8, autoriteit 2%

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;

Antwoord 9, autoriteit 2%

Gebaseerd op het voorbeeld van @ChristianMelchior, hier is mijn oplossing, die twee problemen in de vorige oplossingen oplost:

  • oplossingen die values.xml in de build-directory wijzigen, veroorzaken een volledige herbouw van resources (inclusief aapt van alle tekenbare bestanden)

  • om een ​​onbekende reden verwerkt IntelliJ (en waarschijnlijk Android Studio) de bronnen niet betrouwbaar, waardoor de build niet-vervangen .res-autoproviderautoriteiten

    bevat.

Deze nieuwe oplossing doet dingen meer op de Gradle-manier door een nieuwe taak te maken en maakt incrementele builds mogelijk door invoer- en uitvoerbestanden te definiëren.

  1. maak een bestand (in het voorbeeld plaats ik het in een variantsdirectory), geformatteerd als een resource xml-bestand, dat stringbronnen bevat. Deze worden samengevoegd met de bronnen van de app en elk voorkomen van .res-autoin de waarden wordt vervangen door de pakketnaam van de variant, bijvoorbeeld <string name="search_provider">.res-auto.MySearchProvider</string>

  2. voeg het bestand build_extras.gradletoe van deze kernnaar uw project en verwijs ernaar vanuit de hoofdpagina build.gradledoor apply from: './build_extras.gradle'ergens boven de androidblokkeren

  3. zorg ervoor dat u een standaard pakketnaam instelt door deze toe te voegen aan het android.defaultConfig-blok van build.gradle

  4. verwijs in AndroidManifest.xmlen andere configuratiebestanden (zoals xml/searchable.xmlvoor zoekmachines voor automatisch aanvullen), naar de provider (bijvoorbeeld @string/search_provider)

  5. als u dezelfde naam nodig heeft, kunt u de variabele BuildConfig.PACKAGE_NAMEgebruiken, bijvoorbeeld BuildConfig.PACKAGE_NAME + ".MySearchProvider"

https://gist.github.com/paour/9189462


Update: deze methode werkt alleen op Android 2.2.1 en hoger. Voor eerdere platforms, zie dit antwoord, dat zijn eigen problemen heeft, aangezien de nieuwe manifestfusie nog steeds erg moeilijk is de randen…


Antwoord 10

Ik heb een blogpost geschreven met een Github-voorbeeldproject dat dit probleem (en andere soortgelijke problemen) op een iets andere manier aanpakt dan die van Cyril.

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


Antwoord 11

Helaas lijkt de huidige versie (0.4.1) van de Android-plug-in hier geen goede oplossing voor te bieden. Ik heb nog geen tijd gehad om dit te proberen, maar een mogelijke oplossing voor dit probleem zou zijn om een ​​stringresource @string/provider_authorityte gebruiken en die in het manifest te gebruiken: android:authority="@string/provider_authority". Je hebt dan een res/values/provider.xmlin de res-map van elk buildtype dat de autoriteit zou moeten overschrijven, in jouw geval zou dit src/debug/reszijn

Ik heb gekeken naar het direct genereren van het xml-bestand, maar nogmaals, er lijken geen goede hooks voor te zijn in de huidige versie van de plug-in. Ik raad je echter aan om een ​​functieverzoek in te dienen, ik kan me voorstellen dat meer mensen tegen hetzelfde probleem aan zullen lopen.


Antwoord 12

Het antwoord in dit bericht werkt voor mij.

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

Ik gebruik 3 verschillende smaken, dus ik maak 3 manifesten met content provider in elke smaak, zoals kevinrschultz zei:

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

Je belangrijkste manifest bevat geen providers:

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

En je manifesteert in elke smaak, inclusief provider.

Gratis:

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

Betaald:

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

Overig:

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

Antwoord 13

Waarom voeg je dit niet gewoon toe?

type.packageNameSuffix = “.$type.name”


Antwoord 14

Mijn oplossing is om plaatsaanduidingen te vervangen in AndroidManifest.xml. Het behandelt ook packageNameSuffix-kenmerken, zodat u debugen releasekunt hebben, evenals andere aangepaste builds op hetzelfde apparaat.

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

Ik heb het ook op een gistals je wilt om te zien of het later evolueert.

Ik vond het een elegantere benadering dan de benaderingen voor het ontleden van meerdere bronnen en XML.

Other episodes