Zoptymalizuj szybkość kompilacji

Długi czas kompilacji spowalnia proces programowania. Na tej stronie opisujemy kilka metod, które mogą pomóc i rozwiązywać wąskie gardła szybkości kompilacji.

Ogólny proces zwiększania szybkości kompilacji aplikacji wygląda tak:

  1. Zoptymalizuj konfigurację kompilacji, wykonując kilka czynności, które od razu przyniosą korzyści większości projektów w Android Studio.
  2. Profiluj kompilację, aby wykryć i zdiagnozować z trudniejszymi wąskimi gardłami, które mogą być charakterystyczne dla danego projektu lub stacji.

Podczas tworzenia aplikacji wdróż ją na urządzeniu z Androidem w wersji 7.0 (poziom interfejsu API 24) lub nowszej, jeśli to możliwe. Nowsze wersje Platforma Android wprowadza lepszą mechanikę przekazywania aktualizacji aplikacji, takich jak Android Środowisko wykonawcze (ART) i natywna obsługa wielu plików DEX.

Uwaga: po pierwszej prawidłowej kompilacji możesz zauważyć, że kolejne czystą i przyrostową, działają znacznie szybciej nawet bez optymalizacje opisane na tej stronie. Dzieje się tak, ponieważ demon Gradle ma „rozgrzewkę” okres wzrostu wydajności – podobnie jak w przypadku innych rozwiązań JVM

Zoptymalizuj konfigurację kompilacji

Postępuj zgodnie z tymi wskazówkami, aby ulepszyć kompilację i szybkości projektu w Android Studio.

Zadbaj o aktualność narzędzi

Narzędzia na Androida są optymalizowane pod kątem kompilacji i otrzymują nowe funkcje. każdej aktualizacji. Kilka wskazówek na tej stronie zakłada, że używasz najnowszych wersji. Aby korzystać z najnowszych optymalizacji, zapoznaj się z tymi informacjami: do dnia:

Używaj KSP zamiast kapt

Narzędzie do przetwarzania adnotacji Kotlin (kapt) działa znacznie wolniej niż Kotlin. Procesor symboli (KSP). Jeśli tworzysz źródło Kotlin z adnotacjami i używasz narzędzi, które przetwarza adnotacje (takie jak Sala) który obsługuje KSP, przejdź na KSP.

Unikaj kompilowania niepotrzebnych zasobów

Unikaj kompilowania i pakowania zasobów, których nie testujesz, takich jak o dodatkowe lokalizacje językowe i zasoby dotyczące gęstości ekranu. Zamiast tego podaj tylko jeden element. zasoby językowe i gęstość ekranu na potrzeby Twojego programisty smaku, jak w przykładzie poniżej:

Odlotowe

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

Eksperymentuj z dodaniem portalu wtyczek do Gradle na końcu

W Androidzie wszystkie wtyczki znajdują się w folderach google() oraz mavenCentral() repozytoriów. Jednak Twoja kompilacja może wymagają wtyczek innych firm, które są obsługiwane przez gradlePluginPortal() posprzedażna.

Gradle przeszukuje repozytoria w kolejności, w której są zadeklarowane więc wydajność kompilacji będzie większa, jeśli wymienione repozytoria zawierają z większości wtyczek. Dlatego eksperymentuj z gradlePluginPortal() przez umieszczenie go jako ostatniego w bloku repozytorium w settings.gradle . W większości przypadków pozwala to zminimalizować liczbę zbędnych wyszukiwań wtyczek poprawia szybkość kompilacji.

Więcej informacji o tym, jak Gradle porusza się po wielu repozytoriach, znajdziesz w artykule Deklarowanie wielu repozytoriów w dokumentacji Gradle.

Używanie statycznych wartości konfiguracji kompilacji w kompilacji do debugowania

W przypadku właściwości znajdujących się w pliku manifestu lub plików zasobów zawsze używaj wartości statycznych do debugowania typu kompilacji.

Używanie dynamicznych kodów wersji, nazw wersji, zasobów lub innych elementów inna logika kompilacji, która zmienia plik manifestu, wymaga pełnej kompilacji aplikacji. za każdym razem, gdy chcesz wprowadzić zmiany, nawet jeśli rzeczywiste zmiany w przeciwnym razie wymaga jedynie wymiany „na gorąco”. Jeśli Twoja konfiguracja kompilacji wymaga takiego właściwości dynamicznych, a następnie wyodrębnij je do wariantów kompilacji wersji i zachowaj statyczne wartości kompilacji do debugowania, jak w tym przykładzie:

  ...
  // 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")
    }
  }

Zobacz przepis setVersionsFromTask na GitHubie, aby dowiedzieć się, jak ustawić kod wersji dynamicznej w projekcie.

Użyj wersji zależności statycznej

Podczas deklarowania zależności w plikach build.gradle unikaj używania wersji dynamicznej (takie ze znakiem plusa na końcu, np. 'com.android.tools.build:gradle:2.+'). Używanie dynamicznych numerów wersji może spowodować nieoczekiwane aktualizacje wersji, problemy z rozwiązaniem wersji oraz wolniejsze kompilacje spowodowane sprawdzaniem dostępności aktualizacji przez Gradle. Zamiast tego użyj statycznego numeru wersji.

Tworzenie modułów biblioteki

Poszukaj w aplikacji kodu, który możesz przekonwertować na moduł biblioteki Androida. Taka modularyzacja kodu pozwala systemowi kompilacji skompilować tylko modyfikować i zapisywać w pamięci podręcznej te dane wyjściowe na potrzeby przyszłych kompilacji. Modułyzacja sprawia też, równoległe wykonywanie projektów jest bardziej efektywne, włączyć tę optymalizację.

Utwórz zadania na potrzeby logiki kompilacji niestandardowej

Po utworzeniu profilu kompilacji, jeśli kompilacja pokazuje, że stosunkowo dużo czasu kompilacji jest poświęcona **konfiguracji na etapie projektów**, sprawdź skrypty build.gradle i znajdź do uwzględnienia w niestandardowym zadaniu Gradle. Przez zmianę logiki kompilacji w zadanie, pomagasz zapewnić uruchamianie zadania tylko wtedy, gdy jest to wymagane, wyniki mogą być zapisywane w pamięci podręcznej przez kolejnych kompilacji, które logika kompilacji będzie mogła być uruchamiana równolegle, jeśli włączysz równoległe wykonywanie projektu. Aby dowiedzieć się więcej o taks w przypadku kompilacji niestandardowej przeczytaj oficjalną dokumentację Gradle.

Wskazówka: jeśli kompilacja zawiera dużą liczbę niestandardowych zadań, możesz Chcesz uporządkować pliki build.gradle, tworząc niestandardowe klasy zadań. Dodaj swoje zajęcia do katalog project-root/buildSrc/src/main/groovy/; Gradle automatycznie włącza te klasy do ścieżki klasy w przypadku wszystkich build.gradle plików w projekcie.

Konwertuj obrazy do formatu WebP

WebP to plik graficzny, który zapewnia kompresję stratną (np. JPEG) oraz przezroczystość. (jak PNG). WebP zapewnia lepszą kompresję niż JPEG i PNG.

Zmniejszanie rozmiaru plików graficznych bez konieczności kompresji w czasie kompilacji może przyspieszyć kompilację, zwłaszcza jeśli aplikacja używa i zasobami Google Cloud. Możesz jednak zauważyć niewielki wzrost wykorzystania procesora kompresowania obrazów WebP. Łatwe korzystanie z Android Studio przekonwertuj obrazy do WebP.

Wyłącz crunching PNG

Jeśli nie przekonwertujesz pliku PNG do WebP, nadal możesz przyspieszyć kompilację, wyłączając automatyczne kompresję obrazu podczas tworzenia aplikacji.

Jeśli używasz wtyczki Androida do obsługi Gradle w wersji 3.0.0 lub nowszym, przetwarzanie plików PNG jest domyślnie wyłączone dla funkcji „debugowanie” typu kompilacji. Aby wyłączyć tę funkcję do optymalizacji innych typów kompilacji, dodaj do pliku build.gradle te wiersze:

Odlotowe

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
        }
    }
}

Rodzaje konstrukcji ani smaki produktów nie definiują tej właściwości, więc musisz aby ręcznie ustawić tę właściwość na true podczas tworzenia wersji wersji Twojej aplikacji.

Eksperyment z równoległym czyszczeniem pamięci JVM

Wydajność kompilacji można poprawić, konfigurując optymalny moduł do czyszczenia pamięci JVM używany przez Gradle. Choć pakiet JDK 8 jest skonfigurowany tak, aby domyślnie używał równoległego czyszczenia pamięci, JDK 9 i nowsze wersje skonfigurowano do używania odczyszczacza G1.

Jeśli chcesz potencjalnie zwiększyć wydajność kompilacji: testowania kompilacji Gradle za pomocą równoległego śmieciarek. W sekcji gradle.properties ustaw te wartości:

org.gradle.jvmargs=-XX:+UseParallelGC

Jeśli w tym polu są już ustawione inne opcje, dodaj nową:

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

Aby zmierzyć szybkość kompilacji przy użyciu różnych konfiguracji, zapoznaj się z artykułem Profiluj kompilację

Zwiększ rozmiar sterty JVM

Jeśli zauważysz powolne kompilacje, a proces czyszczenia pamięci masowej zajmuje ponad 15% czas na budowanie Analizator kompilacji , zwiększ rozmiar stosu wirtualnej maszyny Java (JVM). W pliku gradle.properties ustaw limit na 4, 6 lub 8 GB jak w tym przykładzie:

org.gradle.jvmargs=-Xmx6g

Następnie przetestuj ją pod kątem zwiększenia szybkości kompilacji. Najprostszy sposób na określenie optymalnej sterty jest zwiększenie limitu o niewielką ilość, a następnie przetestowanie wystarczającej kompilacji i szybkości działania.

Jeśli używasz też atrybutu równoległy śmieciarek pamięci JVM, to cała linia powinna wyglądać tak:

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

Możesz przeanalizować błędy pamięci JVM, włączając funkcję HeapDumpOnOutOfMemoryError flaga włączona. Dzięki temu, gdy zabraknie pamięci, JVM wygeneruje zrzut stosu.

Użyj nieprzechodniowych klas R

Używaj nieprzechodni klas R, aby szybciej tworzyć kompilacje w przypadku aplikacji z wieloma modułami. Zapobiega to duplikowaniu zasobów przez zapewnienie, klasa R każdego modułu zawiera odwołania tylko do własnych zasobów bez pobierania odniesień jego zależności. Przyspiesza to kompilacje i zapewnia korzyści płynące z kompilacji. i ich unikania. Jest to domyślne zachowanie we wtyczce Androida do obsługi Gradle w wersji 8.0.0 i nowszych.

Począwszy od Android Studio Bumblebee, nieprzechodnie klasy R są domyślnie włączone w nowych projektach. W przypadku projektów utworzonych we wcześniejszych wersjach Android Studio zaktualizuj je, aby korzystały z nieprzechodnich R zajęcia, klikając Refaktoryzacja > Przeprowadź migrację do nieprzenośnych klas R.

Więcej informacji o zasobach aplikacji i klasie R znajdziesz w artykule Omówienie zasobów dotyczących aplikacji.

Użyj niestałych klas R

Użyj stałej klasy R w aplikacjach i testach w celu zwiększenia przyrostu wartości kompilacji w języku Java i pozwalają precyzyjniej ograniczać zasoby. R pól zajęć nie zawsze są stałe dla bibliotek, ponieważ zasoby są numerowane podczas pakowania pakietu APK dla aplikacji lub testu zależnego od tej biblioteki. Jest to domyślne zachowanie we wtyczce Androida do obsługi Gradle w wersji 8.0.0 i nowszych.

Wyłączanie flagi Jetifier

Większość projektów korzysta bezpośrednio z bibliotek AndroidaX, więc możesz usunąć Jetifier, aby zwiększyć wydajność kompilacji. Aby usunąć flagi Jetifiera, ustaw android.enableJetifier=false w gradle.properties.

Analizator kompilacji może sprawdzić, czy flaga może mogą zostać bezpiecznie usunięte, aby umożliwić projektowi lepszą wydajność kompilacji i przeprowadzić migrację i nieobsługujących bibliotek pomocy Androida. Aby dowiedzieć się więcej o Analizatorze kompilacji, zobacz Rozwiązywanie problemów z wydajnością kompilacji

Użyj pamięci podręcznej konfiguracji

pamięć podręczna konfiguracji pozwala Gradle rejestrować informacje o wykresie zadań kompilacji i używać ich w kolejnych kompilacjach. Gradle nie musi ponownie konfigurować całej kompilacji.

Aby włączyć pamięć podręczną konfiguracji, wykonaj te czynności:

  1. Sprawdź, czy wszystkie wtyczki projektu są zgodne.

    Użyj Analizator kompilacji, aby sprawdzić, czy projekt jest zgodny z pamięcią podręczną konfiguracji. Analizator kompilacji uruchamia sekwencję testów aby ustalić, czy daną funkcję można włączyć w projekcie. Zobacz numer problemu 13490: listę obsługiwanych wtyczek.

  2. Dodaj do pliku gradle.properties ten kod:

      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

Gdy pamięć podręczna konfiguracji jest włączona, przy pierwszym uruchomieniu projektu dane wyjściowe kompilacji mówi Calculating task graph as no configuration cache is available for tasks. W trakcie w przypadku kolejnych uruchomień, wynik kompilacji to Reusing configuration cache.

Więcej informacji o pamięci podręcznej konfiguracji znajdziesz w poście na blogu Szczegółowe omówienie buforowania konfiguracji oraz dokumentację Gradle dotyczącą funkcji pamięci podręcznej konfiguracji.

Problemy z pamięcią podręczną konfiguracji wprowadzone w Gradle 8.1 i wtyczce Androida do obsługi Gradle 8.1

Pamięć podręczna konfiguracji stała się stabilna w Gradle 8.1 i wprowadzono interfejs API plików śledzenie konwersji. Połączenia takie jak File.exists(), File.isDirectory() i File.list() są nagrywane przez Gradle do śledzenia plików wejściowych konfiguracji.

Wtyczka Androida do obsługi Gradle (AGP) 8.1 używa tych interfejsów API File w przypadku niektórych plików, które Gradle powinna nie są uznawane za dane wejściowe pamięci podręcznej. Powoduje to dodatkowe unieważnienie pamięci podręcznej, gdy jest używane z Gradle w wersji 8.1 lub nowszej, spowalnia działanie kompilacji. W AGP 8.1 te dane są traktowane jako dane wejściowe w pamięci podręcznej:

Wprowadź tekst Śledzenie problemów Poprawiono w
$GRADLE_USER_HOME/android/FakeDependency.jar Problem nr 289232054 AGP 8.2
dane wyjściowe cmake Problem nr 287676077 AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings Problem nr 278767328 AGP 8.3

Jeśli używasz tych interfejsów API lub wtyczki, która z nich korzysta, może wystąpić pogorszenie czasu kompilacji, ponieważ niektórzy z nich korzystają z tej funkcji logiki może spowodować dodatkowe unieważnienie pamięci podręcznej. Zobacz Ulepszenia śledzenia danych wejściowych konfiguracji kompilacji , aby omówić te wzorce i dowiedzieć się, jak naprawić logikę kompilacji, lub tymczasowo wyłączyć API do śledzenia plików.