Configura varianti della build

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

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

Le varianti di build sono il risultato dell'utilizzo da parte di Gradle di un insieme specifico di regole per combinare impostazioni, codice e risorse configurate nei tipi di build e nelle versioni del prodotto. Anche se non configuri le varianti di build direttamente, sei tu a configurare i tipi di build e le versioni di prodotto che le formano.

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

Configura 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. Anche se il tipo di build di debug non compare nel file di configurazione di 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 archivio chiavi di debug generico.

Puoi aggiungere il tipo di build di debug alla tua configurazione se vuoi aggiungere o modificare determinate impostazioni. L'esempio seguente specifica un applicationIdSuffix per il tipo di build di debug e configura un tipo di build "temporaneo" che viene 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"
        }
    }
}

trendy

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 di compilazione, Android Studio richiede la sincronizzazione del progetto con la nuova configurazione. Per sincronizzare il progetto, fai clic su Sincronizza ora nella barra di notifica che viene visualizzata quando apporti una modifica o fai clic su Sincronizza progetto nella barra degli strumenti. Se Android Studio rileva errori di configurazione, viene visualizzata la finestra Messaggi per descrivere il problema.

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

Configura le versioni dei prodotti

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

Nota: devi comunque specificare un nome di pacchetto utilizzando l'attributo package nel file manifest main/. Devi inoltre utilizzare questo nome del pacchetto nel codice sorgente per fare riferimento alla classe R o per risolvere eventuali attività relative o registrazione del servizio. In questo modo puoi utilizzare applicationId per assegnare a ciascun colore del prodotto un ID univoco per il confezionamento e la distribuzione senza dover modificare il codice sorgente.

Tutti i sapori devono appartenere a una dimensione sapore denominata, ovvero un gruppo di sapori del prodotto. Devi assegnare tutte le versioni a una dimensione; in caso contrario, riceverai il seguente errore di generazione.

  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 flavor, il plug-in Android Gradle assegna automaticamente tutte le versioni flavor del modulo a quella dimensione.

Il seguente esempio di codice crea una dimensione flavor denominata "version" e aggiunge le varianti "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"
        }
    }
}

trendy

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 precedente (creata prima di agosto 2021) che distribuisci utilizzando APK su Google Play, per distribuire la tua app usando il supporto di più APK in Google Play, assegna lo stesso valore applicationId a tutte le varianti e assegna a ogni variante un valore versionCode diverso. Per distribuire diverse varianti della tua app come app separate in Google Play, devi assegnare un valore applicationId diverso a ogni variante.

Dopo aver creato e configurato i sapori dei prodotti, fai clic su Sincronizza ora nella barra delle notifiche. Al termine della sincronizzazione, Gradle crea automaticamente varianti di build in base ai tipi di build e ai gusti del prodotto e le assegna un nome in base a <product-flavor><Build-Type>. Ad esempio, se hai creato versioni di prodotti "demo" e "completa" 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 Crea > Seleziona variante di build 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 creazione taggano l'app con l'ID applicazione definito nel blocco defaultConfig del file build.gradle.kts, come mostrato nell'esempio che segue. Tuttavia, se vuoi creare versioni diverse della tua app in modo che vengano visualizzate come schede separate sul Google Play Store, ad esempio una versione "senza costi" e una versione "Pro", devi creare varianti della build separate, ciascuna con un ID applicazione diverso.

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

Kotlin

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

trendy

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

In questo modo, l'ID applicazione per il gusto di prodotto "senza costi" è "com.example.myapp.free".

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

Kotlin

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

trendy

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

Poiché Gradle applica la configurazione del tipo di build dopo la versione specifica del prodotto, l'ID applicazione per la variante di build "di debug senza costi" è "com.example.myapp.free.debug". Questo è utile quando vuoi avere sia il debug che la build della release sullo stesso dispositivo, perché non due app possono avere lo stesso ID applicazione.

Se hai un'app precedente (creata prima di agosto 2021) che distribuisci utilizzando APK su Google Play e vuoi utilizzare la stessa scheda dell'app per distribuire più APK che hanno come target una configurazione dispositivo diversa, ad esempio il livello API, devi utilizzare lo stesso ID applicazione per ogni variante di build, ma assegnare a ogni APK un valore versionCode diverso. Per ulteriori informazioni, consulta la pagina relativa al supporto di più APK. La pubblicazione utilizzando gli AAB non è interessata, poiché utilizza un singolo artefatto che utilizza un singolo codice 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 Inserire variabili di build nel manifest.

Combina più gusti di prodotto con dimensioni di sapore

In alcuni casi, potresti voler combinare le configurazioni di più versioni. Ad esempio, potresti voler creare configurazioni diverse per le versioni "full" e "demo" dei prodotti basate sul livello API. A questo scopo, il plug-in per Gradle di Android consente di creare più gruppi di gusti di prodotto come dimensioni sapori.

Durante la creazione della tua app, Gradle combina una configurazione della versione di prodotto di ogni dimensione versione che definisci, insieme a una configurazione del tipo di build, per creare la variante di build finale. Gradle non combina sapori dei prodotti che appartengono alla stessa dimensione di sapore.

Il seguente esempio di codice utilizza la proprietà flavorDimensions per creare una dimensione flavor "mode" per raggruppare le versioni "full" e "demo" dei prodotti e una dimensione "api" per raggruppare le configurazioni flavor del 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"
      ...
    }
  }
}
...

trendy

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 versioni in ogni dimensione della versione e al numero di tipi di build configurati. Quando Gradle assegna un nome a ogni variante di build o agli artefatti corrispondenti, vengono visualizzati per primi le varianti di prodotto appartenenti alla dimensione gusto con priorità più alta, seguite da quelle con dimensioni con priorità più bassa e poi 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 build: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • APK corrispondente: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Ad esempio,
    Variante build: minApi24DemoDebug
    APK corrispondente: app-minApi24-demo-debug.apk

Oltre alle directory dei set di origine che puoi creare per ogni singola versione di prodotto e di variante di build, puoi anche creare directory dei set di origine per ogni combinazione di versioni di prodotto. Ad esempio, puoi creare e aggiungere origini Java alla directory src/demoMinApi24/java/ e Gradle le utilizza solo quando crei una variante che combina queste due versioni di prodotti.

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

Filtra varianti

Gradle crea una variante di build per ogni possibile combinazione di versioni e tipi di build di prodotto che configuri. Tuttavia, potrebbero esserci alcune varianti di build non necessarie o che non hanno senso nel contesto del 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 compilazione della sezione precedente, supponi di voler supportare solo i livelli API 23 e successivi per la versione demo dell'app. Puoi utilizzare il blocco variantFilter per filtrare tutte le configurazioni di varianti di build che combinano le versioni 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
        }
    }
}
...

trendy

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 qualsiasi variante della build che soddisfa le condizioni specificate. Le varianti di build non vengono più visualizzate nel menu quando fai clic su Crea > Seleziona variante build dalla barra dei menu o Crea varianti nella barra della finestra degli strumenti.

Crea set di origini

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

Ad esempio, puoi definire la funzionalità di base nel set di origini main/ e utilizzare i set di origini dei gusti del prodotto per modificare il branding della tua app per clienti diversi 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 tuo tipo di build "debug" vengano posizionati nelle directory src/debug/kotlin/ o src/debug/java/.

Il plug-in Android per Gradle fornisce un'attività Gradle utile che ti mostra come organizzare i file per ciascun tipo di build, versioni di prodotto e varianti di build. Ad esempio, il seguente esempio dell'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, procedi come segue:

  1. Fai clic su Valuta 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 di 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 di attività di Gradle durante la sincronizzazione di Gradle.
  3. Dopo che Gradle ha eseguito 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 test per la tua app, ad esempio test/ e androidTest/ i set di origini di test.

Quando crei una nuova variante di build, Android Studio non crea automaticamente le directory dei set sorgente, ma 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 in Gradle SourceSet (Insiemi di origini Gradle), seleziona full/java.
  5. Premi INVIO.

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

Ad esempio, per creare un file XML con i 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 dei valori.
  2. Inserisci il nome del file XML o mantieni il nome predefinito.
  3. Dal menu accanto a Insieme di origini target, seleziona debug.
  4. Fai clic su Fine.

Poiché il tipo di build "debug" è stato specificato come set di origine di destinazione, Android Studio crea automaticamente le directory necessarie al momento della creazione del file XML. La struttura della directory risultante è simile a quella della Figura 1.

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

Gli insiemi di origini attive hanno un indicatore verde nell'icona che indica che sono attivi. Il set di origini debug ha un suffisso con [main] per indicare che verrà unito al set di origini main.

Utilizzando la stessa procedura, puoi anche creare directory dei set di origine per le versioni di prodotto, come src/demo/, e creare varianti, ad esempio src/demoDebug/. Inoltre, puoi creare set di origini di test che hanno come target varianti di build specifiche, come src/androidTestDemoDebug/. Per scoprire di più, consulta la pagina relativa al test dei set di origini.

Modificare le configurazioni degli insiemi di origini predefinite

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

Il blocco sourceSets deve trovarsi nel blocco android. Non è necessario spostare i file di origine; 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 origine. Per sapere quali componenti puoi configurare e se mapparli a più file o directory, consulta la documentazione di riferimento relativa all'API per il plug-in Gradle Android.

Il seguente esempio di codice mappa le origini dalla directory app/other/ a determinati componenti del set di origine main e modifica la directory root del set di origine 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")
      ...
  }
}
...

trendy

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 set di origine. Ad esempio, non puoi condividere le stesse origini di test con entrambi i set di origini test e androidTest. Questo perché Android Studio crea moduli IntelliJ separati per ogni set di origine e non può supportare le directory principali dei contenuti duplicate tra i set di origini.

Crea con set di origini

Puoi utilizzare le directory dei set di origine per contenere il codice e le risorse che vuoi pacchettizzare solo con determinate configurazioni. Ad esempio, se stai creando la variante di build "demoDebug", che è il crossproduct di una versione 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 delle varianti di build)
  2. src/debug/ (set di origine tipo di build)
  3. src/demo/ (insieme di origini dei gusti di prodotto)
  4. src/main/ (insieme di origine principale)

I set di origini creati per le combinazioni di sapori dei prodotti devono includere tutte le dimensioni dei sapori. Ad esempio, il set di origine della variante di build deve essere la combinazione del tipo di build e di tutte le dimensioni flavor. L'unione di codice e risorse che coinvolgono cartelle che coprono più dimensioni versione, ma non tutte, non è supportata.

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

L'ordine di priorità determina quale set di origini 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 della build, se demoDebug/ include un file definito anche in debug/, Gradle utilizza il file nel set di origine demoDebug/. Allo stesso modo, Gradle assegna ai file con tipo di build e origine flavor del prodotto una priorità maggiore rispetto agli stessi file in main/. Gradle prende in considerazione questo ordine di priorità nell'applicazione delle seguenti regole di build:

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

    Nota: per una determinata variante di build, Gradle genera un errore di build se rileva due o più directory dei 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 che src/main/Utility.kt, perché Gradle esamina entrambe le directory durante il processo di compilazione e genera un errore di "classe duplicata". Se vuoi versioni diverse di Utility.kt per tipi di build diversi, ogni tipo di build deve definire la propria versione del file e non includerla nel set di origine main/.

  • I manifest vengono uniti in un unico manifest. La priorità viene assegnata nello stesso ordine dell'elenco nell'esempio precedente. In altre parole, le impostazioni del manifest per un tipo di build sostituiscono le impostazioni del manifest per una versione di prodotto e così via. Per ulteriori informazioni, leggi la sezione relativa all'unione dei file 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 nell'esempio precedente. In altre parole, i valori definiti in un file nel set di origine del tipo di build sostituiscono i valori definiti nello stesso file nella versione di una versione di prodotto e così via.
  • Le risorse nelle directory res/ e asset/ vengono pacchettizzate. Se sono presenti risorse con lo stesso nome definite in due o più set di origini, la priorità viene assegnata nello stesso ordine dell'elenco nell'esempio precedente.
  • Gradle assegna alle risorse e ai manifest inclusi nelle dipendenze dei moduli della libreria la priorità più bassa durante la creazione dell'app.

Dichiara le dipendenze

Per configurare una dipendenza per una variante di build o un set di origine di test specifico, precede il nome della variante di build o del set di origine 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.5.1")
}

trendy

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.5.1'
}

Per saperne di più sulla configurazione delle dipendenze, consulta Aggiungere dipendenze build.

Utilizza la gestione delle dipendenze sensibile alle varianti

Il plug-in Android per Gradle 3.0.0 e versioni successive include un nuovo meccanismo di dipendenza che associa 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 i sapori: la variante freeDebug di un'app consumerà la variante freeDebug di una libreria.

Affinché il plug-in possa abbinare in modo preciso le varianti, devi fornire elementi di riserva corrispondenti, come descritto nella sezione seguente, nei casi in cui non è possibile una corrispondenza diretta.

Ad esempio, supponiamo che la tua app configuri un tipo di build chiamato "temporaneo" e che una delle sue dipendenze di libreria non lo faccia. Quando il plug-in tenta di creare la versione "temporanea" dell'app, non saprà quale versione della libreria utilizzare e verrà visualizzato 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 in che modo Gradle risolve le situazioni in cui non è possibile una corrispondenza diretta delle varianti tra un'app e una dipendenza.

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

  • L'app include un tipo di build diverso da una dipendenza libreria.

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

    Tieni presente che non ci sono problemi quando una dipendenza dalla libreria include un tipo di build diverso dalla tua app. Questo perché il plug-in non richiede mai quel tipo di build dalla dipendenza.

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

    trendy

    // 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 flavor esistente sia nell'app sia nella dipendenza dalla libreria, la tua app include sapori che non sono presenti nella libreria.

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

    Tieni presente che, per una determinata dimensione flavor che esiste sia nell'app sia nelle sue dipendenze di libreria, non esistono problemi quando una libreria include una versione di prodotto diversa dalla tua app. Questo perché il plug-in non richiede mai quel sapore alla dipendenza.

    Usa matchingFallbacks per specificare corrispondenze alternative per il sapore 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")
            }
        }
    }
    

    trendy

    // 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 dalla libreria include una dimensione flavor che non è presente nella tua app.

    Ad esempio, una dipendenza dalla libreria include sapori per una dimensione "minApi", ma la tua app include sapori solo per la dimensione "livello". Se 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 ci sono problemi quando la tua app include una dimensione flavor che non corrisponde a una dipendenza libreria. Questo perché il plug-in corrisponde solo ai gusti delle dimensioni esistenti nella dipendenza. Ad esempio, se una dipendenza non include una dimensione per le ABI, la versione "freeX86Debug" della tua app utilizzerà la versione "freeDebug" della dipendenza.

    Usa missingDimensionStrategy nel blocco defaultConfig per specificare la versione predefinita da parte del plug-in per la selezione da ogni dimensione mancante, come mostrato nell'esempio che segue. Puoi anche eseguire l'override delle tue selezioni nel blocco productFlavors, in modo che ogni versione 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") {}
        }
    }
    

    trendy

    // 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 maggiori informazioni, consulta matchingFallbacks e missingDimensionStrategy nella documentazione di riferimento relativa alla DSL del plug-in Android per Gradle.

Configura le impostazioni di firma

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

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

  1. Creare un archivio chiavi. Un keystore è un file binario che contiene un set di chiavi private. Devi conservare il tuo archivio chiavi in un luogo sicuro.
  2. Creare una chiave privata. Una chiave privata viene utilizzata per firmare l'app per la distribuzione e non viene mai inclusa nell'app o divulgata a terze parti non autorizzate.
  3. Aggiungi la configurazione di 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")
            }
        }
    }

    trendy

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

Nota:l'inclusione delle password per la chiave di rilascio e l'archivio chiavi nel file di build non è una buona prassi di sicurezza. Configura invece il file di build per ottenere queste password dalle variabili di ambiente o fare in modo che il processo di build ti chieda di inserire queste password.

Per ottenere queste password dalle variabili di ambiente:

Kotlin

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

trendy

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

In alternativa, puoi caricare l'archivio chiavi da un file delle proprietà locali. Per motivi di sicurezza, non aggiungere questo file al controllo del codice sorgente. Configura invece localmente per ogni sviluppatore. Per saperne di più, consulta 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 disporre di backup protetti. Se usi la firma dell'app di Google Play e perdi la chiave di caricamento, puoi richiedere una reimpostazione tramite Play Console. Se pubblichi un'app senza 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 alcun aggiornamento dell'app, perché devi sempre firmare tutte le versioni dell'app con la stessa chiave.

Firma delle app per Wear OS

Quando pubblichi app per Wear OS, sia l'APK dell'orologio sia l'APK dello smartphone facoltativo devono essere firmati con la stessa chiave. Per maggiori informazioni sulla confezione e sulla firma delle app per Wear OS, consulta la pagina Confezionare e distribuire le app Wear OS.