Aplikacje na Androida są zwykle kompilowane za pomocą systemu kompilacji Gradle. Zanim zagłębimy się w szczegóły konfigurowania kompilacji, poznamy koncepcje związane z kompilacją, aby umożliwić Ci poznanie całego systemu.
Co to jest kompilacja?
System kompilacji przekształca kod źródłowy w aplikację wykonywalną. Kompilacje często wymagają użycia wielu narzędzi do analizowania, kompilowania, łączenia i pakowania aplikacji lub biblioteki. Gradle porządkuje i uruchamia te polecenia, opierając się na zadaniach.
Zadania zawierają polecenia, które przekładają dane wejściowe na dane wyjściowe. Wtyczki definiują zadania i ich konfigurację. Zastosowanie wtyczki do kompilacji powoduje zarejestrowanie jej zadań i łączenie ich ze sobą za pomocą ich danych wejściowych i wyjściowych. Na przykład zastosowanie w pliku kompilacji wtyczki Androida do obsługi Gradle (AGP) spowoduje zarejestrowanie wszystkich zadań niezbędnych do utworzenia pliku APK lub biblioteki Androida. Wtyczka java-library
umożliwia tworzenie pliku JAR na podstawie 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ą”. Ta konfiguracja jest określona w kilku plikach kompilacji w projekcie (i podprojektach).
Dane wejściowe zadań mogą być plikami i katalogami, a także inne informacje zakodowane w typach Java (całkowite, 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 zadania w inne dane wejściowe zadania łączy zadania ze sobą, tak aby jedno z nich zostało uruchomione przed drugim.
Gradle obsługuje pisanie dowolnego kodu i deklaracji zadań w plikach kompilacji, ale może to utrudniać narzędziom interpretację kompilacji i utrudnia ich obsługę. Możesz na przykład pisać testy kodu w pluginach, ale nie w plikach kompilacji. Zamiast tego ogranicz logikę i deklaracje zadań do wtyczek (zdefiniowanych przez Ciebie lub kogoś innego) i zadeklaruj, jak chcesz korzystać z tej logiki w plikach kompilacji.
Co się dzieje, gdy uruchomisz kompilację Gradle?
Kompilacje Gradle są wykonywane w 3 etapach. Każdy z nich 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 pobierać wtyczki i biblioteki.
- Konfiguracja rejestruje zadania w każdym projekcie i wykonuje plik kompilacji, aby zastosować specyfikację kompilacji użytkownika. Pamiętaj, że kod konfiguracji nie ma dostępu do danych ani plików tworzonych podczas wykonywania.
- Wykonanie to faktyczne „skompilowanie” aplikacji. Wynikiem konfiguracji jest ukierunkowany wykres acykliczny (DAG) zawierający wszystkie wymagane kroki kompilacji, których zażądał użytkownik (zadania dostępne w wierszu poleceń lub jako domyślne w plikach kompilacji). Ten wykres przedstawia relację między zadaniami – jawnie w deklaracji zadania lub na podstawie 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 grafie. 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 Gradle.
Konfiguracja DSL
Gradle używa języka specyficznego dla danej dziedziny (DSL) do konfigurowania kompilacji. To podejście deklaratywne polega na określaniu danych, a nie na tworzeniu szczegółowych instrukcji (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ą rozszerzyć protokół DSL, aby konfigurować dane potrzebne do wykonywania zadań.
Na przykład skonfigurowanie części kompilacji związanej z Androidem może wyglądać 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 // ... } }
W skrócie kod DSL wygląda tak:
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 DSL jest reprezentowany przez funkcję, której konfiguracja wymaga użycia funkcji lambda, oraz właściwość o tej samej nazwie, która uzyskuje do niego dostęp. Dzięki temu kod w plikach kompilacji wygląda bardziej jak specyfikacja 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ą być przeszukane, wersje zależności, których chcesz użyć, a system kompilacji pobiera je podczas kompilacji.
Artefakty Maven są identyfikowane na podstawie nazwy grupy (firmy, dewelopera itp.), nazwy artefaktu (nazwy biblioteki) i wersji danego artefaktu. Jest on zwykle reprezentowany jako group:artifact:version
.
To podejście znacznie usprawnia zarządzanie kompilacji. 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ż modułować swój projekt do podprojektów (nazywanych też „modułami” w Android Studio), których można też używać 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 mają różne opcje. Składają się z typów kompilacji i smaków produktów.
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 do debugowania nie minifikuje ani nie zaciemnia aplikacji, co przyspiesza jej kompilację i zachowuje wszystkie symbole w niezmienionej postaci. Oznacza też aplikację jako możliwą do debugowania, podpisuje ją ogólnym kluczem debugowania i umożliwia dostęp do plików aplikacji zainstalowanych na urządzeniu. Dzięki temu podczas uruchamiania aplikacji możesz przeglądać zapisane dane w plikach i bazach danych.
Kompilacja wersji optymalizuje aplikację, podpisuje ją kluczem wersji i chroni zainstalowane pliki aplikacji.
Za pomocą rodzajów usług możesz zmienić uwzględnione źródło i warianty zależności dla aplikacji. Możesz na przykład utworzyć wersje „demonstryczne” i „pełne” lub „bezpłatne” i „płatne”. Wspólną wersję źródła zapisujesz w katalogu „głównego” zbioru źródeł, a w zbiorze źródeł o nazwie wersji podrzędnej dodajesz nowe źródło lub zastępujesz nim dotychczasowe.
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 te wartości, wariant będzie nosił nazwę <flavor><Buildtype>
. Na przykład w przypadku typów kompilacji release
i debug
oraz smaków demo
i full
interfejs AGP utworzy warianty:
demoRelease
demoDebug
fullRelease
fullDebug
Dalsze kroki
Po zapoznaniu się z koncepcjami kompilacji przyjrzyj się strukturze kompilacji Androida w swoim projekcie.