Выкарыстанне пабудовы тыпаў у Gradle, каб запусціць ж прыкладанне, якое выкарыстоўвае ContentProvider на адным прыладзе

I have set up Gradle to add package name suffix to my debug app so I could have release version that I'm using and debug version on one phone. I was referencing this: 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 улады.

Я бачу адну магчымасць вырашыць гэтую праблему. Калі я правільна разумею, вы павінны быць у стане вызначыць розныя файлы для выкарыстання пры будаўніцтве. Тады я павінен быць у стане паставіць розныя органы ўлады ў розных файлах рэсурсы (і з маніфесту набору паўнамоцтваў у якасці радковага рэсурсу) і сказаць Gradle выкарыстаць іншы рэсурс для адладкі зборкі. Ці магчыма гэта? Калі так, то нейкія намёкі пра тое, як дамагчыся таго, што было б дзіўным!

Ці, можа быць, можна непасрэдна змяніць маніфест з дапамогай Gradle? Любое іншае рашэнне аб тым, як запусціць прыкладанне ж з ContentProvider на адным прыладзе, заўсёды вітаецца.

113
Для тых, хто зацікаўлены ў удасканаленьні уверх па плыні падтрымкі для гэтага выпадку выкарыстання: AOSP паведамленне пра памылку </а>. «Афіцыйная» бягучая пазіцыя заключаецца ў выкарыстанні маніфест найважнейшага рашэння .
дададзена аўтар desseim, крыніца

14 адказы

Ні адзін з існуючых адказаў не задавальняў мяне, аднак свабода была блізкая. Дык вось як я гэта раблю. Перш за ўсё, на дадзены момант я працую з:

  • Android Студыя Beta 0.8.2
  • Gradle убудова 0,12. +
  • <�Літый> Gradle 1.12

Мой <�моцны> мэта з'яўляецца запуск Debug версіі разам з Release версіі на тым жа прыладзе, выкарыстоўваючы адзін і той жа ContentProvider .


У <�моцны> build.gradle вашага прыкладання набору суфікса для адладкі зборкі:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

У <�моцны> AndroidManifest.xml ўсталяваць файл Android: улады ўласцівасць вашага ContentProvider :




У вашым <�моцны> код набор ЎЛАДЫ ўласцівасць, якое можа выкарыстоўвацца ўсюды, дзе неабходна ў вашай рэалізацыі:

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

Tip: Before it was BuildConfig.PACKAGE_NAME

Вось так! Ён будзе працаваць як шарм. Працягвайце чытаць, калі вы карыстаецеся SyncAdapter!


Абнаўленне для SyncAdapter (2014/11/14)

Яшчэ раз я пачну з маёй бягучай налады:

  • Android Студыя Beta 0.9.2
  • Gradle убудова 0.14.1
  • Gradle 2,1

У прынцыпе, калі вам трэба наладзіць некаторыя значэння для розных зборак вы можаце зрабіць гэта з файла build.gradle:

  • выкарыстанне buildConfigField , каб атрымаць доступ да яго з BuildConfig.java клас
  • выкарыстанне resValue , каб атрымаць доступ да яго з рэсурсаў напрыклад @ радок/your_value

У якасці альтэрнатывы для рэсурсаў, вы можаце стварыць асобны buildType або смакавыя каталогі і перавызначыць XMLs ці значэнне ў іх. Тым не менш, я не збіраюся выкарыстоўваць яго ў прыкладзе ніжэй.

прыклад


У <�моцны> 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";

and in build/generated/res/generated/debug/values/generated.xml

<?xml version="1.0" encoding="utf-8"?>


    <!-- Automatically generated file. DO NOT MODIFY -->
    <!-- Values from default config. -->
    your.syncadapter.type.debug
    com.прыклад.app.provider



У сваім <�моцны> authenticator.xml выкарыстанне рэсурсаў, паказаны ў файле build.gradle

<?xml version="1.0" encoding="utf-8"?>


У сваім <�моцны> syncadapter.xml выкарыстоўваюць адзін і той жа рэсурс зноў і @ струнныя/ўлады таксама

<?xml version="1.0" encoding="utf-8"?>

Tip: autocompletion(Ctrl+Space) does not work for these generated resource so you have to type them manually

206
дададзена
@RobMeeuwisse я зрабіў некаторыя змены тут аднак я не мог атрымаць радкі замяніць у sync_adapter. xml ,
дададзена аўтар JJD, крыніца
@Loop Ці вам атрымаецца замяніць ідэнтыфікатар прыкладання ва ўсе .java і XML-файлах? Я да гэтага часу ёсць некаторыя месцы ў гэтым праекце , дзе яна не працуе для мяне.
дададзена аўтар JJD, крыніца
Дзякуючы RobMeeuwisse + Loop: Я паспрабую яго і вярнуцца да вас пасля.
дададзена аўтар JJD, крыніца
@Loop маглі б вы змяніць значэння канфігурацыі адладкі для прыкладу прыкладання, такія як ідэнтыфікатар com.example.myapp.debug ?
дададзена аўтар JJD, крыніца
@Loop - Я не магу атрымаць гэтую працу. Я паспрабаваў кучу рэчаў, але ў канчатковым выніку з java.lang.SecurityException выключэнне: які выклікае UID XXXX адрозніваецца ад UID аутентикатор. Любая ідэя, што можа адбывацца. Мой ACCOUNT_TYPE і аўтарытэт аднолькавыя г.зн. ApplicationID.
дададзена аўтар Pratik Mandrekar, крыніца
любы спосаб, каб прымусіць яго працаваць з searchable.xml?
дададзена аўтар Alf, крыніца
@JJD Змены, якія вы спасылаецеся на будуць працаваць без якога-небудзь карыстацкага сцэнара зборкі. Калі вы хочаце выкарыстоўваць $ {ApplicationID} запаўняльнікі для sync_adapter.xml, authenticator.xml вы павінны наладзіць свой build.gradle сцэнар. Я бачу, што вы ўжо зрабілі шмат у вашым build.gradle сцэнары, так што вы знаёмыя з ідэяй. Вы вынікалі інструкцыям у маім адказ і зрабіў гэта да гэтага часу не працуе?
дададзена аўтар Rob Meeuwisse, крыніца
@JJD Manifest запаўняльнікі працаваць толькі ў AndroidManifest.xml файлах, а не ў іншых файлах, такіх як Рэз/XML/sync_adapter.xml. Для гэтага вам трэба будзе падоўжыць Скрыпт зборкі самастойна. Праверце мой адказ на поўнае праходжанне гульні.
дададзена аўтар Rob Meeuwisse, крыніца
Лепшы адказ ІМХО. Nice кароткі і просты прыклад.
дададзена аўтар rekire, крыніца
Я абнавіў свой адказ з тым, як да для SyncAdapter
дададзена аўтар Damian Petla, крыніца
@BerndS Я адправіў каментар да вашага пытання з растворам. Вы павінны разумець, што змяненне ApplicationID, замяніўшы яго ці ўсталяваць суфікс не ўплывае на Java пакетаў. Гэта проста ідэнтыфікатар вашага прыкладання, і гэта аддзеленае ад пакетаў Java. Глядзіце мой адказ на іншае пытанне, stackoverflow.com/questions/24178007/…
дададзена аўтар Damian Petla, крыніца
@JDD Я не ўпэўнены, якую частку вы мелі на ўвазе і чаму змены неабходныя? Ці не маглі б Вы больш канкрэтна, калі ласка?
дададзена аўтар Damian Petla, крыніца
@ Андрэй Гэта зроблена. Мой адказ абнаўляецца.
дададзена аўтар Damian Petla, крыніца
@PratikMandrekar Было б лепш, калі вы размесціце пытанне на StackOverflow з вашым кодам і XMLs. Затым змесціце спасылку на гэтае пытанне тут, так што я апавешчаны.
дададзена аўтар Damian Petla, крыніца
@JDD Адзіныя месцы, дзе я зрабіў некаторыя змены размешчаны ў маім адказе. У мяне быў хуткі погляд на вашым РЭПО, і я не бачу, выкарыстоўваецца мой падыход.
дададзена аўтар Damian Petla, крыніца
<�Код> BuildConfig.PACKAGE_NAME з'яўляецца састарэлым, вы можаце выкарыстоўваць BuildConfig.APPLICATION_ID замест гэтага. android-review.googlesource.com/#/c/101596
дададзена аўтар Yous, крыніца
Слова парады для тых, хто, выкарыстоўваючы гэты адказ - пераканайцеся, што аператар імпарту для BuildConfig супадае з імем пакета вашага прыкладання. Я меў аператар імпарту для бібліятэкі падтрымкі і мне спатрэбілася некаторы час, каб высветліць, чаму BuildConfig.APPLICATION_ID быў вывад android.support.compat .
дададзена аўтар PPartisan, крыніца
не працуе з searchable.xml
дададзена аўтар axd, крыніца
Так, гэта самы лепшы абыходны шлях я бачыў да гэтага часу. Вялікі дзякуй за абмен! Тым не менш ёсць яшчэ адно пытанне, не звязаны з гэтым, так як мне трэба абнавіць відавочна Intent ў файле preferences.xml, каб выкарыстоўваць новае імя пакета. code.google.com/p/android/issues/detail?id= 57460
дададзена аўтар Bernd S, крыніца
Замест таго, каб мець наканечнік, каб не выкарыстоўваць састарэлую PACKAGE_NAME (які не працуе наогул з версіяй Gradle я толькі што запампаваў), маглі б вы абнавіць код у адказе выкарыстоўваць бягучы пераважны APPLICATION_ID і згадаць PACKAGE_NAME ў кончыках магчыма?
дададзена аўтар Andrew, крыніца
@Loop Дзякуй.
дададзена аўтар Andrew, крыніца

<�Моцны> Новы Android пабудаваць наканечнік сістэмы: ContentProvider ўлада перайменавання

Я думаю, усе вы чулі пра новую сістэму зборкі Android Gradle аснове. Давайце будзем сумленнымі, гэта новая сістэма зборкі ўяўляе сабой велізарны крок наперад у параўнанні з папярэднім. Гэта не з'яўляецца канчатковым яшчэ (на момант напісання артыкула, апошняя версія 0.4.2), але вы ўжо можаце выкарыстоўваць яго бяспечна ў большасці вашых праектаў.

Я personnaly перайшоў большую частку майго праекта новай сістэмы зборкі і былі некаторыя праблемы з-за адсутнасці падтрымкі ў некаторых канкрэтных сітуацыях. Адным з якіх з'яўляецца падтрымкай улады перайменавання 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 пабудаваць сістэму (пакуль? ... Я ўпэўнены, што гэта будзе выпраўлена ў бліжэйшы час). Дык вось шлях:

Па-першае, мы павінны перамясціць пастаўшчыка Android дэкларацыі маніфесту ContentProvider да адпаведнага тыпу зборкі. Для таго, каб зрабіць гэта, мы проста маем:

<�Моцны> SRC/адладжваць/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>


   

       

   

<�Моцны> ЦСІ/выпуск/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>


   

       

   

Пераканайцеся ў тым, каб выдаліць аб'яву ContentProvider з AndroidManifest.xml ў ІП у/асноўнай/таму што 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 і ўсталяваць дзве розныя версіі прыкладання адначасова.


Originaly on Google+: https://plus.google.com/u/0/118417777153109946393/posts/EATUmhntaCQ

38
дададзена
Гэта значна лепш, і да кропкі
дададзена аўтар blueware, крыніца
Для каго-тое, што не можа запусціць Gradle, bacause sintaxy памылкі. Вось адказ: stackoverflow.com/questions/20678118/…
дададзена аўтар Renan Franca, крыніца

У той час як прыклад Кірылы выдатна працуе, калі ў вас ёсць толькі некалькі тыпаў зборкі, яна хутка становіцца складаней, калі ў вас ёсць шмат тыпаў зборкі і/або араматызатараў прадукцыі, як вам трэба, каб падтрымліваць мноства розных AndroidManifest.xml-х гадоў.

Наш праект складаецца з 3-х розных тыпаў будаўнічых і 6 густаў ў агульнай складанасці 18 варыянтаў зборкі, так што замест гэтага мы дадалі падтрымку «.res-аўто» ў ContentProvider ўлады, якія пашыраюцца да бягучага PackageName і ліквідуе неабходнасць падтрымліваць іншую 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 ".res-auto.path.to.provider"
 *
 */
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)
    }
}

Example code can be found here: https://gist.github.com/cmelchior/6988275

23
дададзена
Гэта сапраўды выдатна, дзякуй! Я зрабіў невялікае змяненне, каб прадухіліць паломку з адфарматаваных радкоў. gist.github.com/paour/8475929
дададзена аўтар Pierre-Luc Paour, крыніца
Гэта было вельмі карысна, але я сутыкнуўся з праблемай, дзе яна не будзе будаваць пасля таго, як чыстая, так як не было ні аднаго файла values.xml ў тэчцы зборкі на этапе processManifest. Гэта не існуе да стадыі processResources, у які момант гэта занадта позна, каб змяніць маніфест, так каб замяніць .res-аўто ў абодва маніфесце і каштоўнасці файлаў, я думаю, што вам трэба 2 функцыі, адзін выклікаецца варыянтам. processManifest.doLast, іншыя называюць variant.processResources.doLast.
дададзена аўтар Niall, крыніца
FileWriter робіць праблемы на UTF-8 файлаў, па меншай меры, на маім Mac OS. Я змяніў звязаную радок: ОПР пісьменніка = новы OutputStreamWriter (новы FileOutputStream (pathToFile), "UTF-8")
дададзена аўтар Reza Mohammadi, крыніца
Я перайшоў на выкарыстанне нешта вельмі падобнае на мой праект, а таксама, таму што ў мяне была такая ж праблема з зборкі араматызатараў. Гэты падыход працуе вельмі добра на дадзены момант.
дададзена аўтар MantasV, крыніца

Паколькі убудова версія 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"?>


   

       

   

20
дададзена
Дзякуй за тлумачэнне.
дададзена аўтар rciovati, крыніца
Асцярожна, на аснове рэсурсаў ўлады працуюць толькі на Android 2.2.1 і пазнейшых версій: github.com/android/ platform_frameworks_base/фіксацыі/& hellip;
дададзена аўтар Pierre-Luc Paour, крыніца
працуе для мяне, вялікі дзякуй :)
дададзена аўтар Guillermo Tobar, крыніца
гэта таксама вельмі карысна ў searchable.xml для андроіда: searchSuggestAuthority, таму што вы не можаце выкарыстоўваць $ {ApplicationID}
дададзена аўтар user114676, крыніца

I don't know if anybody mention it. Actually after android gradle plugin 0.10+, the manifest merger will provide the official support for this function: http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger

У AndroidManifest.xml, вы можаце выкарыстоўваць $ {PACKAGENAME}, як гэта:


І ў вашым build.gradle вы можаце мець:

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

See full example here: https://code.google.com/p/anymemo/source/browse/AndroidManifest.xml#152

and here: https://code.google.com/p/anymemo/source/browse/build.gradle#41

13
дададзена
Вы не павінны выкарыстоўваць араматызатары для гэтага, яна працуе з тыпамі зборкі, а таксама. Акрамя таго, было б нядрэнна згадаць, што вы можаце выкарыстоўваць BuildConfig.PACKAGE_NAME, каб атрымаць статычную спасылку на ваш пакет. Гэта карысна для кантэнт-правайдэраў, дзе ўлада павінна быць вядомая падчас выканання для запыту пастаўшчыка кантэнту.
дададзена аўтар Matt Wolfe, крыніца
Гэта добрая навіна, але гэта не здаецца, што гэта комплекснае рашэнне ў выпадку элементаў <�для пошуку>, якія павінны спасылацца на autority, так як яны не з'яўляюцца часткай маніфесту (але існуючым злівацца стратэгіі працуюць для гэтых файлаў, у адрозненне ад маніфесту).
дададзена аўтар Pierre-Luc Paour, крыніца
Варта таксама абноўленыя для выкарыстання $ {ApplicationID} замест $ {PACKAGENAME} для андроіда: улады
дададзена аўтар Bernd S, крыніца

Выкарыстоўвайце $ {ApplicationID} запаўняльнікі ў xml і BuildConfig.APPLICATION_ID у кодзе.

Вам трэба будзе пашырыць сцэнар зборкі, каб уключыць запаўняльнікі ў XML, выдатных маніфесту файлаў. Вы можаце выкарыстоўваць зыходны каталог кожнага варыянту зборкі, каб забяспечыць розныя версіі XML-файлы, але абслугоўванне стане грувасткім вельмі хутка.

<�Моцны> AndroidManifest.xml

Вы можаце выкарыстоўваць запаўняльнік ApplicationID з скрынкі ў маніфесце. Абвясціць пастаўшчык, як гэта:


Звярніце ўвагу на $ {ApplicationID} няшмат. Гэта замяняецца падчас зборкі з фактычнай 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 для варыянту зборкі будуецца.

<�Моцны> Рэз/XML/файлы, напрыклад, syncadapter.xml, accountauthenticator.xml

Калі вы хочаце выкарыстоўваць адаптар сінхранізацыі вам неабходна будзе прадаставіць мета-дадзеныя для ContentProvider і AccountManager ў XML-файлах у Рэз/XML/каталог. Запаўняльнік ApplicationID не падтрымліваецца тут. Але вы можаце пашырыць сцэнар зборкі самастойна ўзламаць яго.




Зноў жа, звернеце ўвагу на $ {ApplicationID} . Гэта працуе толькі калі вы дадасце ніжэй Gradle сцэнар у каранёвым каталогу вашага модуля і ўжываць яго з build.gradle.

<�Моцны> build.gradle

Вырабіце дадатковы сцэнар зборкі з сцэнара модуля build.gradle. Добрае месца ніжэй Android Gradle плагіна.

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

android {
    compileSdkVersion 21
   //etc.

<�Моцны> убудаваны processApplicationId.gradle

Ніжэй працуе крыніца для пабудовы сцэнара дазволу/XML/запаўняльніка. Больш дакументаваны версія даступная на 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

На мой погляд, няма неабходнасці дадаваць падтрымку запаўняльнік для радкоў рэсурсаў. Для прыведзенага вышэй выпадкі выкарыстання прынамсі, ён не патрэбны. Аднак вы можаце лёгка змяніць сцэнар, каб не толькі замяніць запаўняльнікі ў Рэз/XML/каталога, але і ў Рэз/значэнняў/дырэкторыі.

8
дададзена

Я хацеў бы сумесь паміж Кірылам і 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 :



    

        

    

6
дададзена

На аснове ўзору @ChristianMelchior, вось маё рашэнне, якое выпраўляе дзве праблемы ў папярэдніх рашэннях:

  • рашэнні, якія змяняюць values.xml ў каталог зборкі выклікаюць поўнае аднаўленне рэсурсаў (у тым ліку AAPT ўсіх) уводзімага каэфіцыента

  • <�р> па невядомай прычыне, IntelliJ (і, верагодна, Android Studio) не надзейна апрацоўваць рэсурсы, у выніку чаго будаваць, каб змяшчаць не-замяніць .res-аўто Правайдэр ўлады

Гэта новае рашэнне робіць рэчы больш шлях Gradle шляхам стварэння новай задачы і дазваляе інкрэментаванага будуе шляхам вызначэння ўваходных і выходных файлаў.

  1. стварыць файл (у прыкладзе, які я паклаў яго ў Варыянты Каталог), адфарматаваны як XML-файл рэсурсу, які змяшчае радковыя рэсурсы. Яны будуць аб'яднаны ў рэсурсы прыкладання, і любое ўваходжанне .res-аўто у значэнні будзе заменены на імя пакета варыянтнасць, напрыклад <�радок імя = «search_provider»>. рэз-auto.MySearchProvider

  2. дадаць build_extras.gradle файл з гэтай сутнасцю </а> у свой праект і спасылацца на яго з асноўнага кода <> build.gradle , дадаўшы прымяняецца з: «./build_extras.gradle» дзесьці вышэй Android </код > блок </р>

  3. <�р> пераканайцеся, што вы ўсталявалі імя пакетнае па змаўчанні, дадаўшы яго ў android.defaultConfig блок build.gradle

  4. <�Літый> <�р> у AndroidManifest.xml і іншыя канфігурацыйныя файлы (напрыклад, XML/searchable.xml для пошуку пастаўшчыкоў запаўненьня), спасылкі на пастаўшчыка (напрыклад, @ радок/search_provider )
  5. Калі вам трэба, каб атрымаць такое ж імя, вы можаце выкарыстоўваць BuildConfig.PACKAGE_NAME зменнай, напрыклад BuildConfig.PACKAGE_NAME + ".MySearchProvider" </р>

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


Update: this method only works on Android 2.2.1 and later. For earlier platforms, see гэты адказ, which has its own set of problems, since the new manifest merger is still very rough around the edges…
4
дададзена
Асцярожна, на аснове рэсурсаў ўлады працуюць толькі на Android 2.2.1 і пазнейшых версій: github.com/android/ platform_frameworks_base/фіксацыі/& hellip;
дададзена аўтар Pierre-Luc Paour, крыніца
Фіксаваны мая ўласная праблема. Gradle з'яўляецца, каб вырашыць шляху, выкарыстоўваючы System.getProperty ( «user.dir») , які вяртае іншы вынік, калі выклікаецца Android Studio зборкі. Рашэнне складаецца ў тым, каб выкарыстоўваць шлях адносна каталога праекта, які вяртаецца з gradle.startParameter.getProjectDir() . Глядзіце мой каментар у звязанай сутнасці Paour, а таксама.
дададзена аўтар user1978019, крыніца
Дзе вы паставіць свой каталог варыянтаў? У мяне ёсць адзін вялікі Android праект студыі, які залежыць ад некалькіх Android модуляў - маё асноўнага прыкладання і некалькі модуляў Android бібліятэкі. Я магу пабудаваць з каманднага радка, але калі я спрабую пабудаваць ўнутры Android Studio гэта выглядае для варыянты/Рэз-аўта-values.xml адносна /Applications/Android Studio.app/ бен/. гэта значыць я не атрымліваю FileNotFoundException для /Applications/Android Studio.app/bin/variants/res-auto-values.xml . Я бягу на Макінтош. Гэта выдатнае рашэнне, але я хацеў бы, каб прымусіць яго працаваць у IDE для іншых членаў каманды.
дададзена аўтар user1978019, крыніца

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

sync_adapter.xml

<?xml version="1.0" encoding="utf-8"?>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>


    <!-- SyncAdapter and GCM requires a Google account. -->
    
    

    <!-- GCM Creates a custom permission so only this app can receive its messages. -->
    
    

    
            
                
            
            <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator"/>
        
        <!--  -->

        <!-- Sync Adapter -->
        
            
                
            
            <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/sync_adapter" />
        
        <!--  -->

        <!-- Content Provider -->
        
        
        <!--  --> 
    

код:

public static final String CONTENT_AUTHORITY = BuildConfig.CONTENT_AUTHORITY;
public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
4
дададзена

Я напісаў BlogPost з узорам праектам Github, што вырашае гэтую праблему (і іншыя падобныя праблемы) у некалькі іншым выглядзе, чым Кірыл.

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

3
дададзена
Патрэбна добрая! Дзякуй!
дададзена аўтар Tom Susel, крыніца

Unfortunately, the current version (0.4.1) of the android plugin doesn't seem to provide a good solution for this. I haven't had time to try this yet, but a possible workaround for this problem would be to use a string resource @string/provider_authority, and use that in the manifest: android:authority="@string/provider_authority". You then have a res/values/provider.xml in the res folder of each build type that should override the authority, in your case this would be src/debug/res

Я паглядзеў у генерацыі файла xml на ляту, але зноў жа, як уяўляецца, не будзе якіх-небудзь добрыя гаплікі для яе ў бягучай версіі плагіна. Я б рэкамендаваў пакласці ў запыце асаблівасці, хоць, я магу сабе ўявіць, усё больш людзей будуць працаваць у гэтым жа нумар.

2
дададзена
Прывітанне Марк, дзякуй за Ваш адказ. Ваша прапанаванае рашэнне з'яўляецца адзіным, я магу думаць пра сябе цяпер. Але мая праблема, я не ведаю, як дамагчыся таго, што з Gradle.
дададзена аўтар MantasV, крыніца

Адказ на гэты пост працуе для мяне.

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

<!-- Permissions -->

    <!-- Nothing about Content Providers at all -->
    <!-- Activities -->
    ...
    <!-- Services -->
    ...

І ваш маніфест ў вашым кожным водары, уключаючы пастаўшчык.

бясплатна:

<?xml version="1.0" encoding="utf-8"?>


    <!-- Content Providers -->
    
    


выплачана:

<?xml version="1.0" encoding="utf-8"?>


    <!-- Content Providers -->
    
    


іншае:

<?xml version="1.0" encoding="utf-8"?>


    <!-- Content Providers -->
    
    


2
дададзена
Хоць гэтая спасылка можа адказаць на гэтае пытанне, то лепш ўключыць асноўныя часткі адказу тут і даць спасылку для даведкі. Link толькі адказы могуць стаць несапраўднымі, калі звязанымі рэдагаваць старонкі.
дададзена аўтар neelsg, крыніца
не працуе пры выкарыстанні searchable.xml
дададзена аўтар axd, крыніца
Вы маеце рацыю, я рэдагаваў свой адказ. дзякуй :)
дададзена аўтар jcmore2, крыніца

Маё рашэнне заключаецца ў выкарыстанні замены запаўняльніка ў AndroidManifest.xml . Ён таксама апрацоўвае packageNameSuffix атрыбуты, так што вы можаце мець адладкі і рэліз , а таксама любыя іншыя прыстасаваныя зборкі на тым жа прыладзе.

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 разбору падыходаў.

0
дададзена

Чаму б не проста дадаць гэта?

<�Р> type.packageNameSuffix = "$ type.name"
0
дададзена