Configura varianti della build

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

Ogni variante di build rappresenta una versione diversa dell'app che puoi compilare. Ad esempio, potresti creare una versione della tua app senza costi con un insieme limitato di contenuti e un'altra a pagamento che ne includa 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 configurati nei tipi di build e nei gusti dei prodotti. Anche se non configuri direttamente le varianti di build, configuri i tipi di build e le versioni del prodotto che le formano.

Ad esempio, un flavor del 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 compilazione e pacchettistica diverse, come opzioni di debug e chiavi di firma. La variante di compilazione che combina queste due è la versione "demoDebug" dell'app e include una combinazione delle configurazioni e delle risorse incluse nel flavor del prodotto "demo", nel tipo di compilazione "debug" e nel set di origine 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 compilazione di debug e configura un tipo di compilazione "staging" che viene inizializzato utilizzando le impostazioni del tipo di compilazione 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 di compilazione, Android Studio richiede di sincronizzare il progetto con la nuova configurazione. Per sincronizzare il progetto, fai clic su Sincronizza ora nella barra delle notifiche che viene visualizzata quando apporti una modifica oppure fai clic su Sincronizza progetto nella barra degli strumenti. Se Android Studio rileva errori nella 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 la documentazione di riferimento di BuildType.

Configurare le versioni dei prodotti

La creazione di varianti del prodotto è simile alla creazione di tipi di build. Aggiungi le varianti del prodotto al productFlavors blocco nella configurazione di compilazione e includi le impostazioni che preferisci. Le varianti 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 tutti i tipi nel blocco defaultConfig e ogni tipo può modificare uno di questi valori predefiniti, ad esempio applicationId. Per approfondire l'ID applicazione, consulta 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 eventuali registrazioni di attività o servizi correlati. In questo modo puoi utilizzare applicationId per assegnare a ogni versione del 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. In caso contrario, verrà visualizzato 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 solo una dimensione del gusto, il plug-in Gradle per Android assegna automaticamente tutti i gusti del modulo a quella dimensione.

Il seguente esempio di codice crea una dimensione del tipo di prodotto denominata "versione" e aggiunge i tipi di prodotto "demo" e "completo". Questi tipi di analisi 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 precedente (creata prima di agosto 2021) che distribuisci utilizzando gli APK su Google Play, per distribuire la tua app utilizzando il supporto di più APK su 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 su Google Play, devi assegnare un applicationId diverso a ogni variante.

Dopo aver creato e configurato le versioni del prodotto, fai clic su Sincronizza nella barra delle notifiche. Al termine della sincronizzazione, Gradle crea automaticamente le varianti di build in base ai tipi di build e ai gusti del prodotto e li nomina in base a <product-flavor><Build-Type>. Ad esempio, se hai creato i tipi di prodotto "demo" e "full" e hai mantenuto i tipi di build predefiniti "debug" e "release", Gradle crea le seguenti varianti di build:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Per selezionare la variante di build da compilare ed eseguire, vai a Compila > 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 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 conteggiano 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 mostrare come schede separate sul Google Play Store, ad esempio una versione "senza costi" e una "pro", devi creare varianti di build distinte con un ID applicazione diverso.

In questo caso, definisci ogni variante di build come sapore del prodotto distinto. Per ogni flavor 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 versione del prodotto "free" è "com.example.myapp.free".

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

Kotlin

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

Groovy

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

Poiché Gradle applica la configurazione del tipo di build dopo il flavor del prodotto, l'ID applicazione per la variante di build "free debug" è "com.example.myapp.free.debug". Questo è utile quando vuoi avere sia la build di debug sia la build di release sullo stesso dispositivo, perché nessuna delle due app può avere lo stesso ID applicazione.

Se hai un'app precedente (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 ognuno dei quali ha 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 ulteriori informazioni, consulta la pagina sul supporto di più APK. La pubblicazione con gli AAB non è interessata, in quanto 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 compilazione, Gradle sostituisce questo tag con l'ID applicazione effettivo. Per ulteriori informazioni, consulta la sezione Eseguire l'iniezione di variabili di compilazione nel file manifest.

Combinare più gusti dei prodotti con le dimensioni dei gusti

In alcuni casi, potresti voler combinare le configurazioni di più varianti di prodotto. Ad esempio, potresti voler creare configurazioni diverse per le versioni del prodotto "complete" e "demo" in base al livello dell'API. Per farlo, il plug-in Gradle per Android ti consente di creare più gruppi di varianti di prodotto come dimensioni di varianti.

Durante la compilazione dell'app, Gradle combina una configurazione del gusto del prodotto di ogni dimensione del gusto che definisci, insieme a una configurazione del tipo di build, per creare la variante di build finale. Gradle non combina i sapori dei prodotti che appartengono alla stessa dimensione del sapore.

Il seguente esempio di codice utilizza la proprietà flavorDimensions per creare una dimensione del tipo di prodotto "mode" per raggruppare i tipi di prodotto "full" e "demo" e una dimensione del tipo di prodotto "api" per raggruppare le configurazioni dei tipi di prodotto in base al livello dell'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 varianti in ogni dimensione e del numero di tipi di build configurati. Quando Gradle nomina ogni variante di build o gli elementi corrispondenti, i gusti di prodotto appartenenti alla dimensione del gusto con priorità più alta vengono visualizzati per primi, seguiti da quelli delle dimensioni con priorità inferiore e 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 compilazione: [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 degli insiemi di origine che puoi creare per ogni singolo flavor e variante di build del prodotto, puoi anche creare directory degli insiemi di origine per ogni combinazione di flavor del prodotto. Ad esempio, puoi creare e aggiungere sorgenti Java alla directory src/demoMinApi24/java/, e Gradle utilizza queste sorgenti solo quando crea una variante che combina questi due tipi di prodotto.

I set di origini che crei per le combinazioni di varianti del prodotto hanno una priorità più alta rispetto ai set di origini che appartengono a ogni singola variante del prodotto. Per scoprire di più sui set di origine e su come Gradle riunisce le risorse, leggi la sezione su come creare set di origine.

Filtrare le varianti

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

Utilizzando la configurazione di build della sezione precedente come esempio, supponiamo che tu preveda di supportare solo i livelli API 23 e versioni successive per la versione demo dell'app. Puoi utilizzare il blocco variantFilter per filtrare tutte le configurazioni delle varianti di build che combinano i tipi 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 di compilazione e aver fatto clic su Sincronizza nella barra delle notifiche, Gradle ignora le varianti di compilazione che soddisfano le condizioni specificate. Le varianti di compilazione non vengono più visualizzate nel menu quando fai clic su Compila > Seleziona variante di compilazione nella barra dei menu o su Varianti di compilazione nella barra della finestra dello strumento.

Crea set di origini

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

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

Gradle si aspetta che i file e le directory del set di origine siano organizzati in un determinato modo, in 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'utile attività Gradle che mostra come organizzare i file per ogni tipo di build, flavor del prodotto e variante di build. Ad esempio, il seguente esempio di output dell'attività descrive dove Gradle si aspetta di trovare determinati file per il tipo di compilazione "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 LaMiaApplicazione > Attività > Android e fai doppio clic su sourceSets.

    Per visualizzare la cartella Tasks, devi consentire a Gradle di compilare 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 compilare l'elenco di attività 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 origine per i file che vuoi utilizzare per eseguire test per la tua app, ad esempio i set di origine di test test/ e androidTest/.

Quando crei una nuova variante di build, Android Studio non crea le directory del set di origine per te, ma ti offre alcune opzioni utili. Ad esempio, per creare solo la directory java/ per il tipo di compilazione "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. Nel menu sotto Gradle Source Sets (Set di origini Gradle), 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 di 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 quello predefinito.
  3. Nel menu accanto a Set 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 quando crea il file XML. La struttura di directory risultante è simile alla figura 1.

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

Gli insiemi di origini attivi hanno un indicatore verde nell'icona per indicare che sono attivi. Al set di risorse debug viene aggiunto il suffisso [main] per indicare che verrà unito al set di risorse main.

Con la stessa procedura, puoi anche creare directory di set di origine per i gusti dei prodotti, come src/demo/, e creare varianti, come 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ù, consulta la sezione su come testare i set di origine.

Modificare le configurazioni degli insiemi di origini predefinite

Se le origini non sono organizzate nella struttura del file del set di origini predefinito che Gradle si aspetta, come descritto nella sezione precedente sulla creazione di set di origini, puoi utilizzare il blocco sourceSets per modificare la posizione in cui Gradle cerca di raccogliere i file per ogni componente di un set di origini.

Il blocco sourceSets deve essere nel blocco android. Non è necessario riposizionare 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 dell'insieme di origini. Per scoprire quali componenti puoi configurare e se puoi mapparli a più file o directory, consulta la documentazione dell'API del plug-in Gradle per Android.

Il seguente codice di esempio mappa le origini dalla directory app/other/ a determinati componenti dell'insieme di origini main e modifica la directory principale dell'insieme 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 set di origini. Ad esempio, non puoi condividere le stesse origini di test con gli insiemi di origini test e androidTest. Questo accade 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 set di origini

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

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

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

Se combini più sapori di prodotto, la priorità tra i sapori di prodotto è determinata dalla dimensione del sapore a cui appartengono. Quando elenchi le dimensioni del sapore con la proprietà android.flavorDimensions, i sapori dei prodotti che appartengono alla prima dimensione del sapore che elenchi hanno una priorità più alta rispetto a quelli appartenenti alla seconda dimensione del sapore e così via. Inoltre, gli insiemi di origini che crei per combinazioni di varianti di prodotto hanno una priorità superiore rispetto agli insiemi di origini che appartengono a una singola variante di prodotto.

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

  • 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 compilazione se rileva due o più directory del 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 compilazione e genera un errore "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 includerlo nell'insieme di origini 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 manifest per un tipo di compilazione soppiantano le impostazioni manifest per una versione del prodotto e così via. Per saperne di più, consulta l'articolo sulla 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 nell'esempio precedente. In altre parole, i valori definiti in un file nell'origine del tipo di build superano i valori definiti nello stesso file in un flavor del prodotto e così via.
  • Le risorse nelle directory res/ e asset/ vengono pacchettizzate insieme. 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 la priorità più bassa alle risorse e ai manifest inclusi con le dipendenze del modulo della libreria durante la compilazione dell'app.

Dichiarare le dipendenze

Per configurare una dipendenza per una variante di build o un set di origine di test specifico, prefiggi il nome della variante di build o del set di origine di test prima della 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 compilazione.

Utilizza la gestione delle dipendenze basata sulle 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 utilizzi una libreria. Ciò significa che la variante debug di un'app utilizza automaticamente la variante debug di una raccolta e così via. Funziona anche quando si utilizzano i gusti: la variante freeDebug di un'app consumerà la variante freeDebug di una libreria.

Affinché il plug-in corrisponda con precisione alle varianti, devi fornire valori di riserva corrispondenti 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 denominato "staging", ma che una delle sue dipendenze della libreria non lo faccia. Quando il plug-in tenta di compilare la versione "di 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 compilazione 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 tra una variante e una dipendenza.

Di seguito è riportato un elenco di problemi relativi alla corrispondenza delle dipendenze in base 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 verificano problemi quando una dipendenza della libreria include un tipo di compilazione diverso da quello della tua app. Questo accade 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 di seguito:

    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 del gusto esistente sia nell'app sia nella sua dipendenza dalla libreria, l'app include i gusti che la libreria non include.

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

    Tieni presente che per una determinata dimensione del gusto esistente sia nell'app sia nelle sue dipendenze della libreria, non si verificano problemi quando una libreria include un gusto del prodotto che non è presente nell'app. Questo perché il plug-in non richiede mai questa versione dalla dipendenza.

    Utilizza matchingFallbacks per specificare corrispondenze alternative per la versione del prodotto "senza costi " dell'app, come mostrato di seguito:

    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 della libreria include una dimensione del gusto che la tua app non ha.

    Ad esempio, una dipendenza della libreria include i flavor per una dimensione "minApi", ma la tua app include i flavor solo per la dimensione "livello". Quando vuoi compilare 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 verificano problemi se la tua app include una dimensione del gusto non presente in una dipendenza della libreria. Questo accade perché il plug-in abbina i gusti solo delle dimensioni esistenti nella dipendenza. Ad esempio, se una dipendenza non include una dimensione per le ABI, la versione "freeX86Debug" dell'app utilizzerà la versione "freeDebug" della dipendenza.

    Utilizza missingDimensionStrategy nel blocco defaultConfig per specificare il tipo predefinito che il plug-in deve selezionare da ogni dimensione mancante, come mostrato nell'esempio seguente. Puoi anche sostituire le 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") {}
        }
    }

    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 nella documentazione di riferimento DSL del plug-in Android Gradle.

Configura 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 di 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 della release utilizzando le configurazioni di build di Gradle:

  1. Crea un archivio chiavi. Un archivio chiavi è un file binario contenente un insieme di chiavi private. Devi conservare il tuo keystore in un luogo sicuro.
  2. Crea una chiave privata. Per firmare l'app per la distribuzione viene utilizzata una chiave privata, che non viene mai inclusa nell'app o comunicata 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: non è buona norma di sicurezza includere le password per la chiave di rilascio e il keystore all'interno del file di compilazione. Configura invece il file di compilazione in modo da ottenere queste password dalle variabili di ambiente o chiedi che ti vengano chieste durante il processo di compilazione.

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. ma configuralo localmente per ogni sviluppatore. Per scoprire di più, leggi Rimuovere le informazioni sulla firma dai file di compilazione.

Dopo aver completato questa procedura, puoi distribuire l'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 utilizzi la firma dell'app Google Play e perdi la chiave di caricamento, puoi richiedere un ripristino 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 per Wear OS

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