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

Zależność kompilacji to zewnętrzne komponenty wymagane do skompilowania 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. Są to tak zwane zależności przejściowe i mogą szybko zwiększać ogólne zależności wykorzystywane 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 będzie to problemem, ponieważ wiele bibliotek stosuje schemat semantycznego wersjonowania. Biblioteki te ograniczają typy wprowadzanych zmian, aby zapewnić zgodność z wcześniejszymi wersjami.

Wersja semantyczna ma format major.minor.patch. Na przykład w numerze wersji 4.8.3 4 oznacza wersję 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 zmienią się części minor (nowe funkcje) lub patch (poprawki błędów), deweloperzy biblioteki informują, że biblioteka jest nadal zgodna i nie powinna mieć wpływu na Twoją aplikację.

Warto zwracać uwagę na takie zmiany, a w tym celu można użyć kilku narzędzi do aktualizacji zależności.

Relacje w kompilacji

Kompilacje Androida zawierają relacje między:

  • Kod źródłowy – kod i zasoby, nad którymi masz kontrolę
  • zależności biblioteki – zewnętrzne biblioteki lub moduły, które są uwzględniane przez projekt i podprojekty podczas kompilacji;
  • Narzędzia – kompilatory, wtyczki i pakiety SDK, które przekształcają kod źródłowy w aplikację lub bibliotekę.
Tworzenie zależności i ich relacji
Rysunek 1. Buduj relacje

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 zależy od bibliotek (w tym bibliotek środowisk wykonawczych Kotlin i 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 Kotlin Symbol Processor (KSP) lub oddzielne 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. Dzięki temu zmniejsza się liczba zależności, którymi musisz się zajmować. Określasz zależności, których używasz bezpośrednio, a Gradle pobiera je wraz z zależnymi zależnościami. 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 kompilacji (compileSdk). Jest to konieczne, gdy biblioteka używa funkcji dostępnych w pakiecie Android SDK lub jego interfejsów API JDK. Skuteczna minSdk Twojej aplikacji to najwyższa minSdk wymagana przez Twoją aplikację i wszystkie jej bezpośrednie i pośrednie zależności od bibliotek.

Korzystanie z niektórych bibliotek może wymagać użycia określonego wtyczka 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 wirtualną Javii, a jej wtyczki Java wywołują narzędzia Java w JDK.

Wtyczki Gradle

Wtyczki Gradle rozszerzają Gradle przez definiowanie nowych zadań i konfiguracji. Zastosowanie w kompilacji wtyczki umożliwia korzystanie z określonych funkcji kompilacji skonfigurowanych 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 Kotlina udostępnia interfejs API wtyczki, który umożliwia uruchamianie zewnętrznej analizy i generowania kodu bezpośrednio w kompilatorze z dostępem do struktury przeanalizowanego 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. Na przykład prawdopodobnie nie chcesz uwzględniać w opublikowanej aplikacji lub bibliotece bibliotek testów takich jak JUnit, ale chcesz je stosować podczas kompilowania i wykonywania 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 jako ciągi znakó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)
}

Groovy

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

dependencies {
    implementation libs.example.library
}