Configura varianti della build

Questa pagina mostra come configurare le varianti di build per creare versioni diverse dell'app da un unico progetto e come gestire correttamente le dipendenze e le configurazioni di firma.

Ogni variante di build rappresenta una versione diversa dell'app che puoi creare. Ad esempio, potresti creare una versione senza costi della tua app con un insieme limitato di contenuti e un'altra versione a pagamento che ne includa di più. Puoi anche creare versioni diverse della tua app destinate a dispositivi diversi, in base al livello API o ad altre varianti del dispositivo.

Le build variant sono il risultato dell'utilizzo da parte di Gradle di un insieme specifico di regole per combinare impostazioni, codice e risorse configurati nei tipi di build e nelle varianti di prodotto. Anche se non configuri direttamente le varianti di build, configuri i tipi di build e le varianti di prodotto che le compongono.

Ad esempio, un gusto del prodotto "demo" potrebbe specificare determinate funzionalità e requisiti del dispositivo, come codice sorgente, risorse e livelli API minimi, mentre il tipo di build "debug" applica impostazioni di build e packaging diverse, come opzioni di debug e chiavi di firma. La variante di build che combina questi due elementi è la versione "demoDebug" della tua app e include una combinazione delle configurazioni e delle risorse incluse nel flavor di prodotto "demo", nel tipo di build "debug" e nel set di origini main/.

Configurare i tipi di build

Puoi creare e configurare i tipi di build all'interno del blocco android del file build.gradle.kts a livello di modulo. Quando crei un nuovo modulo, Android Studio crea automaticamente i tipi di build di debug e release. Sebbene il tipo di build di debug non venga visualizzato nel file di configurazione della build, Android Studio lo configura con debuggable true. In questo modo puoi eseguire il debug dell'app su dispositivi Android sicuri e configurare la firma dell'app con un keystore di debug generico.

Puoi aggiungere il tipo di build di debug alla configurazione se vuoi aggiungere o modificare determinate impostazioni. Il seguente esempio specifica un applicationIdSuffix per il tipo di build di debug e configura un tipo di build "staging" inizializzato utilizzando le impostazioni del tipo di build di debug:

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

Groovy

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Nota:quando apporti modifiche a un file di configurazione della build, Android Studio richiede di sincronizzare il progetto con la nuova configurazione. Per sincronizzare il progetto, fai clic su Sincronizza ora nella barra di notifica visualizzata quando apporti una modifica o fai clic su Sincronizza progetto dalla barra degli strumenti. Se Android Studio rileva errori nella configurazione, viene visualizzata la finestra Messaggi per descrivere il problema.

Per saperne di più su tutte le proprietà che puoi configurare con i tipi di build, consulta il riferimento BuildType.

Configurare le varianti del prodotto

La creazione di varianti di prodotto è simile alla creazione di tipi di build. Aggiungi le varianti del prodotto al blocco productFlavors nella configurazione della build e includi le impostazioni che preferisci. Le varianti del prodotto supportano le stesse proprietà di defaultConfig, perché defaultConfig appartiene effettivamente alla classe ProductFlavor. Ciò significa che puoi fornire la configurazione di base per tutte le varianti nel blocco defaultConfig e ogni variante può modificare uno qualsiasi di questi valori predefiniti, ad esempio applicationId. Per scoprire di più sull'ID applicazione, leggi Impostare l'ID applicazione.

Nota:devi comunque specificare un nome del pacchetto utilizzando l'attributo package nel file manifest main/. Devi anche utilizzare questo nome del pacchetto nel codice sorgente per fare riferimento alla classe R o per risolvere qualsiasi registrazione relativa di attività o servizi. In questo modo puoi utilizzare applicationId per assegnare a ogni variante di prodotto un ID univoco per il packaging e la distribuzione senza dover modificare il codice sorgente.

Tutti i gusti devono appartenere a una dimensione del gusto denominata, ovvero un gruppo di gusti del prodotto. Devi assegnare tutti i gusti a una dimensione del gusto, altrimenti riceverai il seguente errore di compilazione.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

Se un determinato modulo specifica una sola dimensione di variante, il plug-in Android Gradle assegna automaticamente tutte le varianti del modulo a quella dimensione.

Il seguente esempio di codice crea una dimensione di variante denominata "version" e aggiunge le varianti di prodotto "demo" e "full". Questi gusti forniscono i propri applicationIdSuffix e versionNameSuffix:

Kotlin

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

Groovy

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

Nota:se hai un'app legacy (creata prima di agosto 2021) che distribuisci utilizzando gli APK su Google Play, per distribuire l'app utilizzando il supporto di più APK in Google Play, assegna lo stesso valore applicationId a tutte le varianti e assegna a ogni variante un versionCode diverso. Per distribuire diverse varianti della tua app come app separate in Google Play, devi assegnare un applicationId diverso a ogni variante.

Dopo aver creato e configurato le varianti di prodotto, fai clic su Sincronizza ora nella barra delle notifiche. Una volta completata la sincronizzazione, Gradle crea automaticamente varianti di build in base ai tipi di build e alle varianti di prodotto e le denomina in base a <product-flavor><Build-Type>. Ad esempio, se hai creato le varianti di prodotto "demo" e "full" e hai mantenuto i tipi di build "debug" e "release" predefiniti, Gradle crea le seguenti varianti di build:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Per selezionare la variante di build da creare ed eseguire, vai a Build > Select Build Variant e seleziona una variante di build dal menu. Per iniziare a personalizzare ogni variante di build con le proprie funzionalità e risorse, devi creare e gestire i set di origini, come descritto in questa pagina.

Modificare l'ID applicazione per le varianti di build

Quando crei un APK o un AAB per la tua app, gli strumenti di compilazione taggano l'app con l'ID applicazione definito nel blocco defaultConfig del file build.gradle.kts, come mostrato nell'esempio seguente. Tuttavia, se vuoi creare versioni diverse della tua app da visualizzare come schede separate nel Google Play Store, ad esempio una versione "senza costi" e una "pro", devi creare varianti di build separate, ognuna con un ID applicazione diverso.

In questo caso, definisci ogni variante di build come un sapore del prodotto separato. Per ogni variante all'interno del blocco productFlavors, puoi ridefinire la proprietà applicationId oppure puoi aggiungere un segmento all'ID applicazione predefinito utilizzando applicationIdSuffix, come mostrato di seguito:

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

In questo modo, l'ID applicazione per la variante di prodotto "free" è "com.example.myapp.free".

Puoi anche utilizzare applicationIdSuffix per aggiungere un segmento in base al tipo di build, come mostrato qui:

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Poiché Gradle applica la configurazione del tipo di build dopo la variante del prodotto, l'ID applicazione per la variante di build "debug senza costi" è "com.example.myapp.free.debug". Ciò è utile quando vuoi avere sia la build di debug sia quella di rilascio sullo stesso dispositivo, perché due app non possono avere lo stesso ID applicazione.

Se hai un'app legacy (creata prima di agosto 2021) che distribuisci utilizzando gli APK su Google Play e vuoi utilizzare la stessa scheda dell'app per distribuire più APK che hanno come target una configurazione del dispositivo diversa, ad esempio il livello API, devi utilizzare lo stesso ID applicazione per ogni variante di build, ma assegnare a ogni APK un versionCode diverso. Per maggiori informazioni, leggi l'articolo Supporto di più APK. La pubblicazione utilizzando gli AAB non è interessata, in quanto utilizza un singolo artefatto che utilizza un singolo codice di versione e ID applicazione per impostazione predefinita.

Suggerimento:se devi fare riferimento all'ID applicazione nel file manifest, puoi utilizzare il segnaposto ${applicationId} in qualsiasi attributo manifest. Durante una build, Gradle sostituisce questo tag con l'ID applicazione effettivo. Per ulteriori informazioni, consulta la sezione Inserire variabili di build nel manifest.

Combinare più gusti di prodotti con le dimensioni del gusto

In alcuni casi, potresti voler combinare le configurazioni di più varianti di prodotto. Ad esempio, potresti voler creare configurazioni diverse per le varianti di prodotto "completa" e "demo" in base al livello API. A questo scopo, il plug-in Android Gradle ti consente di creare più gruppi di varianti del prodotto come dimensioni delle varianti.

Quando crei l'app, Gradle combina una configurazione di variante di prodotto di ogni dimensione della variante che definisci, insieme a una configurazione del tipo di build, per creare la variante di build finale. Gradle non combina le varianti di prodotto che appartengono alla stessa dimensione della variante.

Il seguente esempio di codice utilizza la proprietà flavorDimensions per creare una dimensione "mode" per raggruppare le varianti di prodotto "full" e "demo" e una dimensione "api" per raggruppare le configurazioni delle varianti di prodotto in base al livello API:

Kotlin

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.

      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion 23
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion 21
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Il numero di varianti di build create da Gradle è uguale al prodotto del numero di sapori in ogni dimensione del sapore e del numero di tipi di build che configuri. Quando Gradle assegna un nome a ogni variante di build o agli artefatti corrispondenti, le varianti di prodotto appartenenti alla dimensione delle varianti con priorità più alta vengono visualizzate per prime, seguite da quelle delle dimensioni con priorità più bassa, seguite dal tipo di build.

Utilizzando la configurazione di build precedente come esempio, Gradle crea un totale di 12 varianti di build con il seguente schema di denominazione:

  • Variante di build: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • APK corrispondente: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Ad esempio,
    Variante di build: minApi24DemoDebug
    APK corrispondente: app-minApi24-demo-debug.apk

Oltre alle directory dei set di origini che puoi creare per ogni variante di build e ogni sapore di prodotto, puoi anche creare directory dei set di origini per ogni combinazione di sapori di prodotto. Ad esempio, puoi creare e aggiungere origini Java alla directory src/demoMinApi24/java/, e Gradle utilizza queste origini solo quando crea una variante che combina queste due varianti del prodotto.

I set di origini che crei per le combinazioni di gusti dei prodotti hanno una priorità più alta rispetto ai set di origini che appartengono a ogni gusto del prodotto. Per scoprire di più sui set di origini e su come Gradle unisce le risorse, leggi la sezione su come creare set di origini.

Filtrare le varianti

Gradle crea una variante di build per ogni combinazione possibile di flavor del prodotto e tipi di build che configuri. Tuttavia, potrebbero esserci alcune varianti di build che non ti servono o che non hanno senso nel contesto del tuo progetto. Per rimuovere determinate configurazioni delle varianti di build, crea un filtro delle varianti nel file build.gradle.kts a livello di modulo.

Utilizzando come esempio la configurazione di build della sezione precedente, supponiamo che tu preveda di supportare solo i livelli API 23 e successivi per la versione demo dell'app. Puoi utilizzare il blocco variantFilter per filtrare tutte le configurazioni delle varianti di build che combinano i flavor di prodotto "minApi21" e "demo":

Kotlin

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("api", "mode")
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enable = false
        }
    }
}
...

Groovy

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Dopo aver aggiunto un filtro delle varianti alla configurazione della build e aver fatto clic su Sincronizza ora nella barra delle notifiche, Gradle ignora tutte le varianti di build che soddisfano le condizioni specificate. Le varianti di build non vengono più visualizzate nel menu quando fai clic su Build > Select Build Variant (Build > Seleziona variante di build) dalla barra dei menu o su Build Variants (Varianti di build) nella barra della finestra degli strumenti.

Creare set di fonti

Per impostazione predefinita, Android Studio crea il main/ set di origini e le directory per tutto ciò che vuoi condividere tra tutte le varianti di build. Tuttavia, puoi creare nuovi set di origini per controllare esattamente quali file vengono compilati e pacchettizzati da Gradle per tipi di build, varianti di prodotto, combinazioni di varianti di prodotto (quando utilizzi dimensioni delle varianti) e varianti di build specifici.

Ad esempio, puoi definire funzionalità di base nel set di origini main/ e utilizzare i set di origini delle varianti di prodotto per modificare il branding della tua app per diversi clienti oppure includere autorizzazioni speciali e funzionalità di logging solo per le varianti di build che utilizzano il tipo di build di debug.

Gradle prevede che i file e le directory del set di origine siano organizzati in un determinato modo, simile al set di origine main/. Ad esempio, Gradle si aspetta che i file di classe Kotlin o Java specifici per il tipo di build "debug" si trovino nelle directory src/debug/kotlin/ o src/debug/java/.

Il plug-in Android per Gradle fornisce un'attività Gradle utile che mostra come organizzare i file per ciascun tipo di build, variante e versione del prodotto. Ad esempio, il seguente esempio dall'output dell'attività descrive dove Gradle prevede di trovare determinati file per il tipo di build "debug":

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

Per visualizzare questo output:

  1. Fai clic su Gradle nella barra della finestra degli strumenti.
  2. Vai a MyApplication > Tasks > android e fai doppio clic su sourceSets.

    Per visualizzare la cartella Tasks, devi consentire a Gradle di creare l'elenco delle attività durante la sincronizzazione. A tale scopo, procedi nel seguente modo:

    1. Fai clic su File > Impostazioni > Sperimentale (Android Studio > Impostazioni > Sperimentale su macOS).
    2. Deseleziona Non creare l'elenco delle attività Gradle durante la sincronizzazione di Gradle.
  3. Dopo che Gradle esegue l'attività, si apre la finestra Esegui per visualizzare l'output.

Nota:l'output dell'attività mostra anche come organizzare i set di origini per i file che vuoi utilizzare per eseguire i test per la tua app, ad esempio i test/ e androidTest/ set di origini di test.

Quando crei una nuova variante di build, Android Studio non crea le directory dei set di origine, ma ti offre alcune opzioni per aiutarti. Ad esempio, per creare solo la directory java/ per il tipo di build "debug":

  1. Apri il riquadro Progetto e seleziona la visualizzazione Progetto dal menu nella parte superiore del riquadro.
  2. Vai a MyProject/app/src/.
  3. Fai clic con il tasto destro del mouse sulla directory src e seleziona Nuovo > Directory.
  4. Dal menu sotto Gradle Source Sets, seleziona full/java.
  5. Premi Invio.

Android Studio crea una directory del set di origine per il tipo di build di debug e poi crea la directory java/ al suo interno. In alternativa, Android Studio può creare le directory per te quando aggiungi un nuovo file al progetto per una variante di build specifica.

Ad esempio, per creare un file XML dei valori per il tipo di build "debug":

  1. Nel riquadro Progetto, fai clic con il tasto destro del mouse sulla directory src e seleziona Nuovo > XML > File XML valori.
  2. Inserisci il nome del file XML o mantieni il nome predefinito.
  3. Dal menu accanto a Target Source Set, seleziona debug.
  4. Fai clic su Fine.

Poiché il tipo di build "debug" è stato specificato come set di origini di destinazione, Android Studio crea automaticamente le directory necessarie quando crea il file XML. La struttura di directory risultante è simile a quella della figura 1.

Figura 1. Nuove directory del set di origini per il tipo di build "debug".

I set di fonti attivi hanno un indicatore verde nell'icona per mostrare che sono attivi. Il set di risorse debug ha il suffisso [main] per indicare che verrà unito al set di risorse main.

Utilizzando la stessa procedura, puoi anche creare directory di set di origini per varianti di prodotto, ad esempio src/demo/, e creare varianti di build, ad esempio src/demoDebug/. Inoltre, puoi creare set di origini di test che hanno come target varianti di build specifiche, ad esempio src/androidTestDemoDebug/. Per saperne di più, leggi l'articolo Testare i set di origini.

Modificare le configurazioni predefinite dei set di origini

Se hai origini non organizzate nella struttura predefinita dei file del set di origini prevista da Gradle, come descritto nella sezione precedente sulla creazione di set di origini, puoi utilizzare il blocco sourceSets per modificare la posizione in cui Gradle cerca i file per ogni componente di un set di origini.

Il blocco sourceSets deve trovarsi nel blocco android. Non è necessario spostare i file sorgente; devi solo fornire a Gradle i percorsi, relativi al file build.gradle.kts a livello di modulo, in cui Gradle può trovare i file per ogni componente del set di origini. Per scoprire quali componenti puoi configurare e se puoi mapparli a più file o directory, consulta la documentazione di riferimento dell'API del plug-in Android Gradle.

Il seguente esempio di codice mappa le origini dalla directory app/other/ a determinati componenti del set di origini main e modifica la directory principale del set di origini androidTest:

Kotlin

android {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("androidTest") {
      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
  }
}
...

Groovy

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Tieni presente che una directory di origine può appartenere a un solo insieme di origini. Ad esempio, non puoi condividere le stesse fonti di test con i set di fonti test e androidTest. Questo perché Android Studio crea moduli IntelliJ separati per ogni set di origine e non può supportare radici dei contenuti duplicate nei set di origine.

Creare con i set di origini

Puoi utilizzare le directory dei set di origine per contenere il codice e le risorse che vuoi includere nel pacchetto solo con determinate configurazioni. Ad esempio, se stai creando la variante di build "demoDebug", che è il prodotto incrociato di una variante di prodotto "demo" e di un tipo di build "debug", Gradle esamina queste directory e assegna loro la seguente priorità:

  1. src/demoDebug/ (set di origini della variante di compilazione)
  2. src/debug/ (set di origini del tipo di build)
  3. src/demo/ (insieme di origini delle versioni prodotto)
  4. src/main/ (set di fonti principale)

I set di origini creati per le combinazioni di gusti dei prodotti devono includere tutte le dimensioni del gusto. Ad esempio, l'insieme di origini della variante di build deve essere la combinazione del tipo di build e di tutte le dimensioni delle varianti. L'unione di codice e risorse che coinvolgono cartelle che coprono più dimensioni di variante, ma non tutte, non è supportata.

Se combini più gusti di prodotto, la priorità tra i gusti di prodotto è determinata dalla dimensione a cui appartengono. Quando elenchi le dimensioni del sapore con la proprietà android.flavorDimensions, i sapori del prodotto che appartengono alla prima dimensione del sapore che elenchi hanno una priorità maggiore rispetto a quelli che appartengono alla seconda dimensione del sapore e così via. Inoltre, i set di origini che crei per le combinazioni di gusti del prodotto hanno una priorità maggiore rispetto ai set di origini che appartengono a un singolo gusto del prodotto.

L'ordine di priorità determina quale insieme di sorgenti ha una priorità maggiore quando Gradle combina codice e risorse. Poiché la directory del set di origine demoDebug/ contiene probabilmente file specifici per quella variante di build, se demoDebug/ include un file definito anche in debug/, Gradle utilizza il file nel set di origine demoDebug/. Analogamente, Gradle assegna una priorità più alta ai file nei set di origine del tipo di build e della variante del prodotto rispetto agli stessi file in main/. Gradle considera questo ordine di priorità quando applica le seguenti regole di build:

  • Tutto il codice sorgente nelle directory kotlin/ o java/ viene compilato insieme per generare un unico output.

    Nota:per una determinata variante di build, Gradle genera un errore di build se rileva due o più directory di set di origine che hanno definito la stessa classe Kotlin o Java. Ad esempio, quando crei un'app di debug, non puoi definire sia src/debug/Utility.kt sia src/main/Utility.kt, perché Gradle esamina entrambe queste directory durante il processo di build e genera un errore "classe duplicata". Se vuoi versioni diverse di Utility.kt per diversi tipi di build, ogni tipo di build deve definire la propria versione del file e non includerlo nel set di origini main/.

  • I manifest vengono uniti in un unico manifest. La priorità viene assegnata nello stesso ordine dell'elenco nell'esempio precedente. ovvero le impostazioni del manifest per un tipo di build sostituiscono le impostazioni del manifest per una variante del prodotto e così via. Per saperne di più, leggi l'articolo sull'unione dei manifest.
  • I file nelle directory values/ vengono uniti. Se due file hanno lo stesso nome, ad esempio due file strings.xml, la priorità viene assegnata nello stesso ordine dell'elenco dell'esempio precedente. ovvero i valori definiti in un file nel set di origini del tipo di build ignorano i valori definiti nello stesso file in una variante del prodotto e così via.
  • Le risorse nelle directory res/ e asset/ vengono pacchettizzate insieme. Se in due o più set di origini sono definite risorse con lo stesso nome, la priorità viene assegnata nello stesso ordine dell'elenco dell'esempio precedente.
  • Gradle assegna la priorità più bassa a risorse e manifest inclusi nelle dipendenze dei moduli della libreria durante la creazione dell'app.

Dichiarare le dipendenze

Per configurare una dipendenza per una variante di build specifica o un insieme di origini di test, anteponi il nome della variante di build o dell'insieme di origini di test prima della parola chiave Implementation, come mostrato nell'esempio seguente:

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Per ulteriori informazioni sulla configurazione delle dipendenze, consulta Aggiungere dipendenze di build.

Utilizzare la gestione delle dipendenze in base alle varianti

Il plug-in Android per Gradle 3.0.0 e versioni successive include un nuovo meccanismo di dipendenza che abbina automaticamente le varianti quando viene utilizzata una libreria. Ciò significa che la variante debug di un'app utilizza automaticamente la variante debug di una libreria e così via. Funziona anche quando si utilizzano varianti: la variante freeDebug di un'app utilizzerà la variante freeDebug di una libreria.

Affinché il plug-in possa trovare le varianti con precisione, devi fornire i fallback di corrispondenza come descritto nella sezione seguente, per i casi in cui non è possibile una corrispondenza diretta.

Ad esempio, supponiamo che la tua app configuri un tipo di build chiamato "staging", ma una delle sue dipendenze della libreria non lo faccia. Quando il plug-in tenta di creare la versione "staging" della tua app, non saprà quale versione della libreria utilizzare e visualizzerai un messaggio di errore simile al seguente:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Risolvere gli errori di build relativi alla corrispondenza delle varianti

Il plug-in include elementi DSL per aiutarti a controllare il modo in cui Gradle risolve le situazioni in cui non è possibile una corrispondenza diretta tra una variante di un'app e una dipendenza.

Di seguito è riportato un elenco di problemi relativi alla corrispondenza delle dipendenze sensibili alle varianti e come risolverli utilizzando le proprietà DSL:

  • La tua app include un tipo di build che una dipendenza di libreria non include.

    Ad esempio, la tua app include un tipo di build "staging", ma una dipendenza include solo i tipi di build "debug" e "release".

    Tieni presente che non si verifica alcun problema quando una dipendenza della libreria include un tipo di build che la tua app non include. Questo perché il plug-in non richiede mai questo tipo di build dalla dipendenza.

    Utilizza matchingFallbacks per specificare corrispondenze alternative per un determinato tipo di build, come mostrato qui:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
  • Per una determinata dimensione di variante presente sia nell'app sia nella relativa dipendenza di libreria, l'app include varianti che la libreria non include.

    Ad esempio, sia la tua app sia le sue dipendenze di libreria includono una dimensione di variante "livello". Tuttavia, la dimensione "livello" nell'app include le varianti "senza costi" e "a pagamento", mentre una dipendenza include solo le varianti "demo" e "a pagamento" per la stessa dimensione.

    Tieni presente che per una determinata dimensione di variante presente sia nell'app che nelle relative dipendenze di libreria, non si verifica alcun problema quando una libreria include una variante di prodotto non presente nella tua app. Questo perché il plug-in non richiede mai questa variante dalla dipendenza.

    Utilizza matchingFallbacks per specificare corrispondenze alternative per la variante di prodotto "senza costi " dell'app, come mostrato qui:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
  • Una dipendenza di libreria include una dimensione di variante che la tua app non ha.

    Ad esempio, una dipendenza di libreria include varianti per una dimensione "minApi", ma la tua app include varianti solo per la dimensione "livello". Quando vuoi creare la versione "freeDebug" della tua app, il plug-in non sa se utilizzare la versione "minApi23Debug" o "minApi18Debug" della dipendenza.

    Tieni presente che non si verifica alcun problema quando la tua app include una dimensione di variante che una dipendenza della libreria non include. Questo perché il plug-in corrisponde solo alle varianti delle dimensioni che esistono nella dipendenza. Ad esempio, se una dipendenza non include una dimensione per le ABI, la versione "freeX86Debug" della tua app utilizzerebbe la versione "freeDebug" della dipendenza.

    Utilizza missingDimensionStrategy nel blocco defaultConfig per specificare il sapore predefinito da cui il plug-in deve scegliere per ogni dimensione mancante, come mostrato nell'esempio seguente. Puoi anche ignorare le selezioni nel blocco productFlavors, in modo che ogni variante possa specificare una strategia di corrispondenza diversa per una dimensione mancante.

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }

Per ulteriori informazioni, consulta matchingFallbacks e missingDimensionStrategy nel riferimento DSL del plug-in Android Gradle.

Configurare le impostazioni di firma

Gradle non firma l'APK o l'AAB della build di release, a meno che tu non definisca esplicitamente una configurazione della firma per questa build. Se non hai ancora una chiave di firma, genera una chiave di caricamento e un keystore utilizzando Android Studio.

Per configurare manualmente le configurazioni di firma per il tipo di build di rilascio utilizzando le configurazioni di build Gradle:

  1. Crea un keystore. Un archivio chiavi è un file binario che contiene un insieme di chiavi private. Devi conservare il keystore in un luogo sicuro.
  2. Crea una chiave privata. Una chiave privata viene utilizzata per firmare la tua app per la distribuzione e non viene mai inclusa nell'app o divulgata a terze parti non autorizzate.
  3. Aggiungi la configurazione della firma al file build.gradle.kts a livello di modulo:

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

Nota:includere le password per la chiave di rilascio e il keystore all'interno del file di build non è una buona pratica di sicurezza. Configura invece il file di build per ottenere queste password dalle variabili di ambiente o per fare in modo che il processo di build ti chieda queste password.

Per ottenere queste password dalle variabili di ambiente:

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

In alternativa, puoi caricare il keystore da un file di proprietà locale. Per motivi di sicurezza, non aggiungere questo file al controllo del codice sorgente. Configuralo invece localmente per ogni sviluppatore. Per saperne di più, leggi Rimuovere le informazioni di firma dai file di build.

Dopo aver completato questa procedura, puoi distribuire la tua app e pubblicarla su Google Play.

Avviso: conserva l'archivio chiavi e la chiave privata in un luogo sicuro e assicurati di averne eseguito il backup in modo sicuro. Se utilizzi la firma delle app Play e perdi la chiave di caricamento, puoi richiedere una reimpostazione utilizzando Play Console. Se pubblichi un'app senza la firma dell'app di Google Play (per le app create prima di agosto 2021) e perdi la chiave di firma dell'app, non potrai pubblicare aggiornamenti dell'app, poiché devi sempre firmare tutte le versioni dell'app con la stessa chiave.

Firma delle app Wear OS

Quando pubblichi app per Wear OS, sia l'APK per lo smartwatch sia l'APK per lo smartphone facoltativo devono essere firmati con la stessa chiave. Per maggiori informazioni sulla pacchettizzazione e sulla firma delle app Wear OS, vedi Pacchettizzare e distribuire app Wear.