Omówienie kompilacji Gradle

Aplikacje na Androida są zwykle kompilowane za pomocą systemu kompilacji Gradle. Zanim przejdziemy do szczegółów konfigurowania wersji, omówimy założenia, na których ona się opiera, abyś miał/a pełniejszy obraz systemu.

Co to jest kompilacja?

System kompilacji przekształca kod źródłowy w plik wykonywalny. Kompilacje często wymagają użycia wielu narzędzi do analizowania, kompilowania, łączenia i pakowania aplikacji lub biblioteki. Gradle używa podejścia opartego na zadaniach do porządkowania i uruchamiania tych poleceń.

Zadanie zawiera polecenia, które przekształcają dane wejściowe w dane wyjściowe. Wtyczki definiują zadania i ich konfigurację. Zastosowanie w kompilacji wtyczki rejestruje jej zadania i połącza je za pomocą ich danych wejściowych i wyjściowych. Na przykład zastosowanie wtyczki Androida do obsługi Gradle (AGP) do pliku kompilacji spowoduje zarejestrowanie wszystkich zadań niezbędnych do skompilowania pliku APK lub biblioteki Androida. Wtyczka java-library umożliwia tworzenie pliku JAR z kodu źródłowego Java. Istnieją podobne wtyczki do Kotlina i innych języków, ale inne wtyczki służą do rozszerzania wtyczek. Na przykład wtyczka protobuf ma dodać obsługę protokołu protobuf do dotychczasowych wtyczek, takich jak AGP czy java-library.

Gradle preferuje konwencję nad konfiguracją, więc wtyczki będą mieć domyślne wartości domyślne, ale możesz dodatkowo skonfigurować kompilację za pomocą deklaratywnego języka domeny (DSL). Dzięki temu możesz określić co budować, a nie jak. Logika w wtyczkach zarządza „jakością”. Konfiguracja jest określana w kilku plikach kompilacjiprojekcie (i podprojektach).

Dane wejściowe zadania mogą być plikami i katalogami, a także innymi informacjami zakodowanymi jako typy Java (całki, ciągi znaków lub klasy niestandardowe). Dane wyjściowe mogą być tylko katalogami lub plikami, ponieważ muszą być zapisane na dysku. Połączenie danych wyjściowych jednego zadania z danymi wejściowymi innego zadania powoduje połączenie tych zadań, co oznacza, że jedno z nich musi zostać wykonane przed drugim.

Gradle obsługuje pisanie dowolnego kodu i deklaracji zadań w plikach kompilacji, ale może to utrudniać narzędziom interpretację kompilacji i jej utrzymywanie. Możesz na przykład pisać testy kodu w pluginach, ale nie w plikach kompilacji. Zamiast tego ogranicz deklaracje logiki kompilacji i zadań do wtyczek (zdefiniowanych przez Ciebie lub przez kogoś innego) i określ, jak chcesz używać tej logiki w plikach kompilacji.

Co się dzieje, gdy uruchomisz kompilację Gradle?

Kompilacje Gradle są wykonywane w 3 etapach. Każda z tych faz wykonuje różne części kodu zdefiniowane w plikach kompilacji.

  • Inicjalizowanie określa, które projekty i podprojekty są uwzględniane w kompilacji, oraz konfiguruje ścieżki klas zawierające pliki kompilacji i zastosowane wtyczki. Na tym etapie skupiasz się na pliku ustawień, w którym deklarujesz projekty do skompilowania oraz lokalizacje, z których chcesz pobrać wtyczki i biblioteki.
  • Konfiguracja rejestruje zadania dla każdego projektu i wykonuje plik kompilacji, aby zastosować specyfikację kompilacji użytkownika. Pamiętaj, że kod konfiguracji nie będzie mieć dostępu do danych ani plików wygenerowanych podczas wykonywania.
  • Wykonanie to faktyczne „skompilowanie” aplikacji. Dane wyjściowe konfiguracji to kierowany graf acykliczny zadań, reprezentujący wszystkie wymagane przez użytkownika kroki kompilacji (czyli zadania podane w wierszu poleceń lub jako domyślne w plikach kompilacji). Ten wykres przedstawia relacje między zadaniami, które są wyraźnie określone w deklaracji zadania lub wynikają z jego danych wejściowych i wyjściowych. Jeśli zadanie ma dane wejściowe, które są danymi wyjściowymi innego zadania, musi być wykonane po tym zadaniu. W tej fazie zadania, które są nieaktualne, są wykonywane w kolejności zdefiniowanej w grafu. Jeśli dane wejściowe zadania nie zmieniły się od jego ostatniego wykonania, Gradle je pominie.

Więcej informacji znajdziesz w artykule Cykl tworzenia kreacji w Gradle.

Konfiguracja DSL

Gradle używa języka specyficznego dla danej dziedziny (DSL) do konfigurowania kompilacji. To podejście deklaratywnie skupia się na określaniu danych, a nie na pisaniu instrukcji krok po kroku (imperatywnych). Pliki kompilacji możesz pisać w języku Kotlin lub Groovy, ale zdecydowanie zalecamy użycie Kotlina.

Języki te mają ułatwiać wszystkim, zarówno ekspertom w danej dziedzinie, jak i programistom, pracę nad projektem. Definiują one mały język, który przedstawia dane w bardziej naturalny sposób. Wtyczki Gradle mogą rozszerzać DSL, aby konfigurować dane potrzebne do wykonania swoich zadań.

Konfigurowanie części kompilacji na Androida może wyglądać na przykład tak:

Kotlin

android {
    namespace = "com.example.app"
    compileSdk = 34
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk = 34
        // ...
    }
}

Groovy

android {
    namespace 'com.example.myapplication'
    compileSdk 34
    // ...

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdk 24
        // ...
    }
}

Za kulisami kod DSL jest podobny do:

fun Project.android(configure: ApplicationExtension.() -> Unit) {
    ...
}

interface ApplicationExtension {
    var compileSdk: Int
    var namespace: String?

    val defaultConfig: DefaultConfig

    fun defaultConfig(configure: DefaultConfig.() -> Unit) {
        ...
    }
}

Każdy blok w języku DSL jest reprezentowany przez funkcję, która przyjmuje funkcję lambda do konfiguracji i właściwość o tej samej nazwie, aby uzyskać do niej dostęp. Dzięki temu kod w plikach kompilacji przypomina specyfikację danych.

Zależności zewnętrzne

System kompilacji Maven wprowadził specyfikację zależności, system przechowywania i zarządzania. Biblioteki są przechowywane w repozytoriach (serwerach lub katalogach) wraz z metadanymi, w tym ich wersją i zależnościami od innych bibliotek. Określasz repozytoria, które mają zostać przeszukane, wersje zależności, których chcesz użyć, a system kompilacji pobiera je podczas kompilacji.

Artefakty Maven są identyfikowane za pomocą nazwy grupy (firma, deweloper itp.), nazwy artefaktu (nazwa biblioteki) i wersji tego artefaktu. Jest on zwykle reprezentowany jako group:artifact:version.

Takie podejście znacznie ułatwia zarządzanie kompilacją. Takie repozytoria są często nazywane „repozytoriami Maven”, ale chodzi tu o sposób pakowania i publikowania artefaktów. Te repozytoria i metadane zostały wykorzystane w kilku systemach kompilacji, w tym w Gradle (który może publikować w tych repozytoriach). Publiczne repozytoria umożliwiają udostępnianie wszystkim, a repozytoria firmowe przechowują wewnętrzne zależności.

Możesz też modularyzować swój projekt, dzieląc go na podprojekty (nazywane też w Android Studio „modułami”), które mogą też służyć jako zależności. Każdy podprojekt generuje dane wyjściowe (np. pliki JAR), które mogą być wykorzystywane przez podprojekty lub projekt najwyższego poziomu. Może to skrócić czas kompilacji, ponieważ pozwala określić, które części należy ponownie skompilować, a także lepiej rozdzielić obowiązki w aplikacji.

Więcej informacji o określaniu zależności znajdziesz w sekcji Dodawanie zależności kompilacji.

Tworzenie wariantów

Podczas tworzenia aplikacji na Androida zwykle trzeba utworzyć kilka wariantów. Warianty zawierają inny kod lub są tworzone z użyciem innych opcji. Składają się z różnych typów kompilacji i wersji produktu.

Typy kompilacji różnią się deklarowanymi opcjami kompilacji. Domyślnie AGP konfiguruje typy wersji „release” i „debug”, ale możesz je dostosować i dodać więcej (np. do testów stagingowych lub wewnętrznych).

Kompilacja debugowa nie kompresuje ani nie zaciemnia aplikacji, co przyspiesza jej kompilację i zachowuje wszystkie symbole w niezmienionej postaci. Oznacza też aplikację jako „możliwą do debugowania”, podpisując ją za pomocą ogólnego klucza debugowania i umożliwiając dostęp do zainstalowanych plików aplikacji na urządzeniu. Dzięki temu podczas uruchamiania aplikacji możesz przeglądać zapisane dane w plikach i bazach danych.

Wersja wydania optymalizuje aplikację, podpisuje ją kluczem wersji i chroni pliki zainstalowanej aplikacji.

Za pomocą wersji produktu możesz zmienić zawarte źródło i warianty zależności w aplikacji. Możesz na przykład utworzyć wersje „demo” i „pełną” swojej aplikacji lub wersje „bezpłatną” i „płatną”. Wspólną wersję źródła zapisujesz w katalogu „głównego” zbioru źródeł, a w zbiorze źródeł o nazwie wersji podrzędnej zastępujesz lub dodajesz źródło.

AGP tworzy warianty dla każdej kombinacji typu kompilacji i wersji produktu. Jeśli nie zdefiniujesz smaków, warianty zostaną nazwane na podstawie typów kompilacji. Jeśli zdefiniujesz obie, wariant będzie miał nazwę <flavor><Buildtype>. Na przykład w przypadku typów kompilacji releasedebug oraz wersji demofull AGP utworzy te warianty:

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

Dalsze kroki

Po zapoznaniu się z koncepcjami dotyczącymi kompilacji zapoznaj się ze strukturą kompilacji Androida w projekcie.