Ottimizza la velocità delle build

Tempi di build lunghi rallentano il processo di sviluppo. Questa pagina fornisce alcune tecniche per aiutare a risolvere i colli di bottiglia della velocità di build.

La procedura generale per migliorare la velocità di build della tua app è la seguente:

  1. Ottimizza la configurazione della build seguendo alcuni passaggi che saranno utili immediatamente per la maggior parte dei progetti Android Studio.
  2. Profila la tua build per identificare e diagnosticare alcuni dei colli di bottiglia più complicati che potrebbero essere specifici del tuo progetto o della tua workstation.

Quando sviluppi la tua app, eseguine il deployment su un dispositivo con Android 7.0 (livello API 24) o versioni successive, se possibile. Le versioni più recenti della piattaforma Android implementano meccanismi migliori per il push degli aggiornamenti alla tua app, ad esempio Android Runtime (ART) e supporto nativo per più file DEX.

Nota: dopo la prima build pulita, potresti notare che le build successive, sia pulita che incrementale, vengono eseguite molto più velocemente anche senza utilizzare nessuna delle ottimizzazioni descritte in questa pagina. Questo perché il daemon Gradle ha un periodo di "preparazione" di aumento delle prestazioni, simile ad altri processi JVM.

Ottimizza la configurazione della build

Segui questi suggerimenti per migliorare la velocità di build del tuo progetto Android Studio.

Tieni aggiornati gli strumenti

Gli strumenti Android ricevono ottimizzazioni delle build e nuove funzionalità quasi a ogni aggiornamento. Alcuni suggerimenti in questa pagina presuppongono che tu stia utilizzando la versione più recente. Per sfruttare le ottimizzazioni più recenti, tieni aggiornate quanto segue:

Usa il principale punto di forza anziché kapt

Lo strumento di elaborazione delle annotazioni Kotlin (kapt) è molto più lento rispetto al processore di simboli Kotlin (KSP). Se stai scrivendo un'origine Kotlin annotata e stai utilizzando strumenti per l'elaborazione delle annotazioni (come Room) che supportano il principale punto di forza, ti consigliamo di eseguire la migrazione al principale punto di forza.

Evita di compilare risorse non necessarie.

Evita di compilare e pacchettizzare risorse che non stai testando, ad esempio ulteriori localizzazioni linguistiche e risorse relative alla densità dello schermo. Specifica invece solo una risorsa di lingua e una densità di schermo per la versione "dev", come mostrato nell'esempio seguente:

Trendy

android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}

Kotlin

android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}

Prova a posizionare il Portale plug-in Gradle all'ultimo

In Android, tutti i plug-in si trovano nei repository google() e mavenCentral(). Tuttavia, la tua build potrebbe richiedere plug-in di terze parti che vengono risolti utilizzando il servizio gradlePluginPortal().

Gradle cerca nei repository nell'ordine in cui sono dichiarati, quindi le prestazioni della build sono migliorate se i repository elencati per primi contengono la maggior parte dei plug-in. Quindi, prova la voce gradlePluginPortal() inserendola per ultima nel blocco di repository nel file settings.gradle. Nella maggior parte dei casi, questo riduce al minimo il numero di ricerche di plug-in ridondanti e migliora la velocità della build.

Per ulteriori informazioni su come Gradle esplora più repository, consulta Dichiarazione di più repository nella documentazione di Gradle.

Utilizzare valori di configurazione della build statici con la build di debug

Utilizza sempre valori statici per le proprietà contenute nel file manifest o nei file di risorse per il tipo di build di debug.

L'utilizzo di codici di versione dinamici, nomi di versione, risorse o qualsiasi altra logica di build che modifichi il file manifest richiede una build dell'app completa ogni volta che vuoi eseguire una modifica, anche se la modifica effettiva potrebbe richiedere solo un hot swap. Se la configurazione della build richiede queste proprietà dinamiche, isola queste proprietà in base alle varianti della build della release e mantieni statici i valori per le build di debug, come mostrato nell'esempio seguente:

  ...
  // Use a filter to apply onVariants() to a subset of the variants.
  onVariants(selector().withBuildType("release")) { variant ->
      // Because an app module can have multiple outputs when using multi-APK, versionCode
      // is only available on the variant output.
      // Gather the output when we are in single mode and there is no multi-APK.
      val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

      // Create the version code generating task.
      val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
          it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt"))
      }

      // Wire the version code from the task output.
      // map will create a lazy Provider that:
      // 1. Runs just before the consumer(s), ensuring that the producer (VersionCodeTask) has run
      //    and therefore the file is created.
      // 2. Contains task dependency information so that the consumer(s) run after the producer.
      mainOutput.versionCode.set(versionCodeTask.flatMap { it.outputFile.map { it.asFile.readText().toInt() } })
  }
  ...

  abstract class VersionCodeTask : DefaultTask() {

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun action() {
        outputFile.get().asFile.writeText("1.1.1")
    }
  }

Consulta la ricetta setVersionsFromTask su GitHub per scoprire come impostare un codice di versione dinamico nel tuo progetto.

Utilizza versioni delle dipendenze statiche

Quando dichiari le dipendenze nei file build.gradle, evita di utilizzare numeri di versione dinamici (quelli con un segno più alla fine, come 'com.android.tools.build:gradle:2.+'). L'utilizzo di numeri di versione dinamici può causare aggiornamenti imprevisti delle versioni, difficoltà a risolvere le differenze di versione e build più lente a causa del controllo Gradle della disponibilità di aggiornamenti. Utilizza invece numeri di versione statici.

Creare moduli della libreria

Cerca nell'app codice che puoi convertire in un modulo della libreria di Android. La modularizzazione del codice in questo modo consente al sistema di compilazione di compilare solo i moduli modificati e di memorizzare nella cache gli output per le build future. La modularizzazione rende inoltre più efficace l'esecuzione parallela di un progetto quando abiliti questa ottimizzazione.

Crea attività per la logica di build personalizzata

Dopo aver creato un profilo di build, se il profilo mostra che una parte relativamente lunga del tempo viene dedicata alla fase di **configurazione dei progetti**, esamina gli script build.gradle e cerca il codice da includere in un'attività Gradle personalizzata. Spostando parte della logica di build in un'attività, puoi assicurarti che l'attività venga eseguita solo quando necessario, che i risultati possano essere memorizzati nella cache per le build successive e che la logica di build diventi idonea all'esecuzione in parallelo se attivi l'esecuzione parallela del progetto. Per scoprire di più sugli strumenti per la logica di build personalizzata, leggi la documentazione ufficiale di Gradle.

Suggerimento: se la tua build include un numero elevato di attività personalizzate, può essere utile mettere in ordine i file build.gradle creando classi di attività personalizzate. Aggiungi i tuoi corsi alla directory project-root/buildSrc/src/main/groovy/. Gradle include automaticamente questi corsi nel percorso della classe per tutti i build.gradle file del tuo progetto.

Converti le immagini in WebP

WebP è un formato file immagine che fornisce compressione con perdita di dati (come JPEG) e trasparenza (come PNG). WebP può fornire una compressione migliore rispetto a JPEG o PNG.

La riduzione delle dimensioni dei file immagine senza dover eseguire la compressione al momento della build può velocizzare le build, soprattutto se la tua app utilizza molte risorse immagine. Tuttavia, potresti notare un piccolo aumento nell'utilizzo della CPU del dispositivo durante la decompressione delle immagini WebP. Utilizza Android Studio per convertire facilmente le immagini in WebP.

Disattiva analisi dei file PNG

Se non converti le immagini PNG in WebP, puoi comunque velocizzare la build disattivando la compressione automatica delle immagini ogni volta che crei l'app.

Se utilizzi il plug-in Android per Gradle 3.0.0 o versioni successive, il processo di analisi dei file PNG è disattivato per impostazione predefinita per il tipo di build "debug". Per disabilitare questa ottimizzazione per altri tipi di build, aggiungi quanto segue al file build.gradle:

Trendy

android {
    buildTypes {
        release {
            // Disables PNG crunching for the "release" build type.
            crunchPngs false
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the "release" build type.
            isCrunchPngs = false
        }
    }
}

Poiché i tipi di build o le versioni di prodotto non definiscono questa proprietà, devi impostare manualmente questa proprietà su true quando crei la versione di release dell'app.

Sperimenta con il garbage collector parallelo di JVM

Le prestazioni della build possono essere migliorate configurando il garbage collector JVM ottimale utilizzato da Gradle. Mentre JDK 8 è configurato per l'utilizzo del garbage collector parallelo per impostazione predefinita, JDK 9 e versioni successive sono configurati per l'utilizzo del garbage collector G1.

Per migliorare potenzialmente le prestazioni della build, ti consigliamo di testare le build Gradle con il garbage collector parallelo. In gradle.properties, imposta quanto segue:

org.gradle.jvmargs=-XX:+UseParallelGC

Se in questo campo sono già impostate altre opzioni, aggiungine una nuova:

org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC

Per misurare la velocità della build con diverse configurazioni, consulta Profilare la build.

Aumenta la dimensione heap JVM

Se noti build lente, e in particolare la garbage collection richiede più del 15% del tempo di compilazione nei risultati dell'analizzatore build, devi aumentare la dimensione dello heap di Java Virtual Machine (JVM). Nel file gradle.properties, imposta il limite su 4, 6 o 8 gigabyte, come mostrato nell'esempio seguente:

org.gradle.jvmargs=-Xmx6g

Quindi testa il miglioramento della velocità di build. Il modo più semplice per determinare la dimensione heap ottimale è aumentare il limite di una piccola quantità e quindi testare un miglioramento sufficiente della velocità di build.

Se utilizzi anche il garbage collector parallelo JVM, l'intera riga dovrebbe avere il seguente aspetto:

org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g

Puoi analizzare gli errori di memoria JVM attivando il flag HeapDumpOnOutOfMemoryError. In questo modo, la JVM genererà un dump dell'heap quando esaurisci la memoria.

Usare classi R non transitive

Utilizza classi R non transitive per creare build più veloci per app con più moduli. In questo modo si evita la duplicazione delle risorse garantendo che la classe R di ogni modulo contenga solo riferimenti alle proprie risorse senza estrarre riferimenti dalle sue dipendenze. Questo porta a build più veloci e ai corrispondenti vantaggi di evitare la compilazione. Questo è il comportamento predefinito nel plug-in Android per Gradle 8.0.0 e versioni successive.

A partire da Bumblebee di Android Studio, le classi R non transitive sono attive per impostazione predefinita per i nuovi progetti. Per i progetti creati con versioni precedenti di Android Studio, aggiornali in modo da utilizzare classi R non transitive in Refactoring > Migrate to non-transitive R Classes.

Per scoprire di più sulle risorse per le app e sul corso R, consulta Panoramica delle risorse per le app.

Utilizzare classi R non costanti

Utilizza campi classe R non costanti in app e test per migliorare l'incrementalità della compilazione Java e consentire una riduzione più precisa delle risorse. I campi della classe R non sono sempre costanti per le librerie, poiché le risorse sono numerate durante la pacchettizzazione dell'APK per l'app o il test che dipende dalla libreria. Questo è il comportamento predefinito nel plug-in Android per Gradle 8.0.0 e versioni successive.

Disattiva il flag Jetifier

Poiché la maggior parte dei progetti utilizza direttamente le librerie AndroidX, puoi rimuovere il flag Jetifier per migliorare le prestazioni della build. Per rimuovere il flag Jetifier, imposta android.enableJetifier=false nel file gradle.properties.

L'analizzatore build può eseguire un controllo per verificare se è possibile rimuovere in sicurezza il flag per consentire al progetto di migliorare le prestazioni della build e di eseguire la migrazione dalle librerie di assistenza Android non gestite. Per saperne di più sullo strumento di analisi della build, consulta Risolvere i problemi relativi alle prestazioni delle build.

Utilizza la cache di configurazione

La cache di configurazione consente a Gradle di registrare informazioni sul grafico delle attività di build e di riutilizzarle in build successive. In questo modo Gradle non deve riconfigurare l'intera build.

Per abilitare la cache di configurazione:

  1. Verifica che tutti i plug-in del progetto siano compatibili.

    Utilizza lo Strumento di analisi build per verificare se il progetto è compatibile con la cache di configurazione. L'analizzatore di build esegue una sequenza di build di test per determinare se la funzionalità può essere attivata per il progetto. Consulta il problema n. 13490 per un elenco dei plug-in supportati.

  2. Aggiungi il seguente codice al file gradle.properties:

      org.gradle.configuration-cache=true
      # Use this flag carefully, in case some of the plugins are not fully compatible.
      org.gradle.configuration-cache.problems=warn

Quando la cache di configurazione è abilitata, la prima volta che esegui il progetto l'output della build indica Calculating task graph as no configuration cache is available for tasks. Durante le esecuzioni successive, l'output della build indica Reusing configuration cache.

Per saperne di più sulla cache di configurazione, consulta il post del blog Approfondimento sulla memorizzazione nella cache di configurazione e la documentazione di Gradle sulla cache di configurazione.

Problemi della cache di configurazione introdotti in Gradle 8.1 e Android Gradle Plugin 8.1

La cache di configurazione è diventata stabile in Gradle 8.1 e ha introdotto il monitoraggio dell'API dei file. Chiamate come File.exists(), File.isDirectory() e File.list() vengono registrate da Gradle per tenere traccia dei file di input di configurazione.

Il plug-in Android per Gradle (AGP) 8.1 utilizza queste API File per alcuni file che Gradle non dovrebbe considerare come input della cache. Ciò attiva un'ulteriore invalidazione della cache se utilizzato con Gradle 8.1 e versioni successive, rallentando le prestazioni della build. Quelli riportati di seguito vengono trattati come input della cache in AGP 8.1:

Ingresso Monitoraggio problemi Corretto in
$GRADLE_USER_HOME/android/FakeDependency.jar Problema n. 289232054 AGP 8.2
output cmake Problema n. 287676077 AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings Problema n. 278767328 AGP 8.3

Se usi queste API o un plug-in che le utilizza, potresti riscontrare una regressione nella durata della build, perché una logica di build che utilizza queste API può attivare un'ulteriore convalida della cache. Consulta i miglioramenti del monitoraggio dell'input della configurazione di build per un'analisi di questi pattern e su come correggere la logica di build oppure disattiva temporaneamente il monitoraggio dell'API del file.