Współzależności między narzędziami i bibliotekami

Zależności kompilacji to komponenty zewnętrzne wymagane do udanej kompilacji projektu. Kompilacja może zależeć od bibliotek, wtyczek i podprojektów, pakietu SDK Androida, narzędzi takich jak kompilatory KotlinJava, środowisk programistycznych takich jak Android Studio oraz samego Gradle.

Każda zależność może wymagać innych zależności. Nazywamy je zależnościami pośrednimi i mogą one szybko zwiększać łączną liczbę zależności używanych przez aplikację. Gdy chcesz uaktualnić zależność, np. bibliotekę, narzędzie lub pakiet SDK Androida, ta aktualizacja może być przeprowadzona kaskadowo, co spowoduje zaktualizowanie wielu innych zależności.

Często nie powoduje to żadnych problemów, ponieważ wiele bibliotek korzysta ze schematu znanego jako semantyczna obsługa wersji. Biblioteki te ograniczają typy wprowadzanych zmian, aby zapewnić zgodność z wcześniejszymi wersjami.

Wersja semantyczna ma format major.minor.patch. Na przykład w wersji 4.8.3 4 to wersja major, 8 to wersja minor, a 3 to numer patch. Gdy zmieni się część major, biblioteka może zawierać zmiany w interfejsie API lub w zachowaniu, które mogą mieć wpływ na działanie aplikacji. Może to wpłynąć na zachowanie kompilacji lub aplikacji.

Gdy elementy minor (nowe funkcje) lub patch (poprawki błędów) ulegną zmianie, deweloperzy biblioteki poinformują Cię, że biblioteka jest nadal zgodna i nie powinna mieć wpływu na Twoją aplikację.

Ważne jest, aby śledzić takie zmiany. Pomóc w tym mogą kilka narzędzi do uaktualniania.

Relacje w kompilacji

Kompilacje w Androidzie obejmują relacje między:

  • Kod źródłowy – kod i zasoby, nad którymi masz kontrolę
  • Zależności bibliotek – zewnętrzne biblioteki lub moduły uwzględnione w projekcie i podprojektach
  • Narzędzia – kompilatory, wtyczki i pakiety SDK, które przekształcają kod źródłowy w aplikację lub bibliotekę.
Tworzenie zależności i relacji między nimi
Rys. 1. budowanie relacji

Kod źródłowy

Kod źródłowy to kod Kotlin lub Java napisany w aplikacji lub bibliotece. (informacje o używaniu C++ znajdziesz w Android NDK).

Kod źródłowy korzysta z bibliotek (w tym Kotlin i bibliotek środowiska wykonawczego Java) oraz pakietu Android SDK i wymaga odpowiedniego kompilatora Kotlin lub Java.

Niektóre źródła kodu zawierają adnotacje, które wymagają dodatkowego przetwarzania. Jeśli na przykład piszesz kod Jetpack Compose, dodaj adnotacje (takie jak @Composable), które mają być przetwarzane przez wtyczkę kompilatora Compose Kotlin. Inne adnotacje mogą być przetwarzane przez procesor Kotlin Symbol CPU (KSP) lub osobne narzędzia do przetwarzania adnotacji.

Zależności bibliotek

Biblioteki zawierają bajtkod pobrany w ramach aplikacji. Może to być plik JAR w języku Java, biblioteka Androida (AAR) lub podprojekt w kompilacji. Wiele bibliotek stosuje wersje semantyczne, co ułatwia zrozumienie, czy po uaktualnieniu pozostaną one zgodne (czy też nie).

Biblioteki mogą być zależne od innych bibliotek, które są używane ponownie. Takie zależności nazywamy pośrednimi. Ogranicza to zależności, którymi musisz jednoznacznie zarządzać. Wystarczy, że określisz zależności, których używasz bezpośrednio, a Gradle pobierze je razem z zależnościami pośrednimi. Pamiętaj, że gdy aktualizujesz bezpośrednie zależności, mogą one zaktualizować te zależności transitive.

Czasami biblioteka może wymagać minimalnej wersji pakietu Android SDK w czasie działania (minSdk) lub podczas kompilowania (compileSdk). Jest to konieczne, gdy biblioteka korzysta z funkcji zawartych w pakiecie Android SDK lub udostępnionych w nim interfejsach JDK API. Efektywny minSdk Twojej aplikacji to najwyższy obiekt minSdk zażądany przez aplikację wraz ze wszystkimi zależnościami bibliotek bezpośrednich i przechodnich.

Korzystanie z niektórych bibliotek może wymagać użycia określonej wtyczki Gradle. Te wtyczki pomocnicze często instalują procesory symboli Kotlina lub inne procesory adnotacji, które generują kod lub modyfikują kompilację Twoich źródeł, aby umożliwić korzystanie z funkcji biblioteki. Na przykład Jetpack Room zawiera adnotacje i moduł KSP, który przekształca je w wygenerowany kod służący do pobierania i modyfikowania danych w bazie danych. Jetpack Compose wymaga wtyczki kompilatora Compose, aby modyfikować funkcje z adnotacją, aby zarządzać sposobem i czasem ich ponownego wykonywania.

Narzędzia

Gradle

Gradle to narzędzie do kompilacji, które odczytuje pliki kompilacji i wygeneruje aplikację lub bibliotekę. Udostępnia też interfejs API dla wtyczek, aby rozszerzyć możliwości Gradle. Gradle uruchamia wiele procesów na co najmniej 1 maszynie wirtualnej Java, a jego wtyczki Java wywołują narzędzia Java wewnątrz pakietu JDK.

Wtyczki Gradle

Wtyczki Gradle rozszerzają możliwości Gradle, definiując nowe zadania i konfigurację. Zastosowanie wtyczki do kompilacji umożliwia korzystanie z określonych możliwości kompilacji, które są skonfigurowane jako dane w skryptach kompilacji. W przypadku kompilacji na Androida najważniejsza jest wtyczka Androida do obsługi Gradle (AGP).

Kompilatory

Kompilator Kotlin lub Java przekształca kod źródłowy w wykonywalny kod bajtowy. Kompilator Kotlin ujawnia interfejs API wtyczki, który umożliwia uruchamianie analizy zewnętrznej i generowania kodu bezpośrednio w kompilacji, uzyskując dostęp do przeanalizowanej struktury kodu.

Wtyczki kompilatora

Wtyczki kompilatora przeprowadzają analizę i generowanie kodu wewnątrz kompilatora Kotlin, gdy kompilator Kotlin analizuje kod, i są instalowane podczas stosowania w kompilacji wtyczek Gradle.

Android SDK

Pakiet SDK Androida zawiera interfejsy API platformy Android i Java dla konkretnej wersji Androida oraz odpowiednie narzędzia. Te narzędzia ułatwiają zarządzanie pakietem SDK, tworzenie aplikacji oraz komunikowanie się z urządzeniami z Androidem i ich emulowanie.

Każda wersja pakietu SDK Androida udostępnia konkretne interfejsy API Javy, do których może uzyskać dostęp kod źródłowy, oraz obsługę desugaringu, która umożliwia korzystanie z tych interfejsów API w starszych wersjach Androida.

JDK

Java Development Kit (JDK) zawierający biblioteki i pliki wykonywalne Java do kompilowania kodu źródłowego w języku Java oraz uruchamiania aplikacji w tym języku. W kompilacji Androida jest kilka wersji JDK. Więcej informacji znajdziesz w artykule Java w wersjach Androida.

Zakresy Gradle

Gradle grupowanie zależności bibliotek w różne zakresy (nazywane w interfejsie Gradle API konfiguracjami), co pozwala określić różne zestawy zależności bibliotek do użycia w różnych częściach procesu kompilacji. Prawdopodobnie nie chcesz na przykład włączać bibliotek testowych (np. JUnit) w opublikowanej aplikacji lub bibliotece, ale używać ich przy tworzeniu i przeprowadzaniu testów jednostkowych. Za pomocą zakresów możesz też dodawać procesory symboli lub adnotacji, aby analizować kod.

Na przykład AGP definiuje zakresy implementation i api, które umożliwiają określenie, czy zależność ma być widoczna dla użytkowników podprojektu. W artykule Konfigurowanie zależności znajdziesz opisy tych i innych zakresów używanych w kompilacji Androida.

Dodaj zależności bibliotek w bloku dependencies plików kompilacji w postaci ciągów group:artifact:version:

Kotlin

// In a module-level build script
// explicit dependency strings ("group:artifact:version")
dependencies {
    implementation("com.example:library1:1.2.3")
    api("com.example:library2:1.1.1")
}

Groovy

// In a module-level build script
// explicit dependency strings ("group:artifact:version")
dependencies {
    implementation 'com.example:library1:1.2.3'
    api 'com.example:library2:1.1.1'
}

lub w katalogu wersji:

# Version catalog - gradle/libs.versions.toml
[versions]
exampleLib = "1.2.3"
examplePlugin = "2.3.4"

[libraries]
example-library = { group = "com.example", name = "library", version.ref = "exampleLib" }

[plugins]
example-plugin = { id = "com.example.plugin", version.ref = "examplePlugin" }

i określ wygenerowane zmienne w plikach kompilacji:

Kotlin

// In a module-level build script
// Using a version catalog
plugins {
    alias(libs.plugins.example.plugin)
}

dependencies {
    implementation(libs.example.library)
}

Odlotowe

// In a module-level build script
// Using a version catalog
plugins {
    alias(libs.plugins.example.plugin)
}

dependencies {
    implementation libs.example.library
}