Wtyczka Androida do obsługi Gradle (AGP) to oficjalny system kompilacji aplikacji na Androida. Umożliwia kompilowanie wielu różnych typów źródeł i łączenie ich w aplikację, którą można uruchomić na fizycznym urządzeniu z Androidem lub w emulatorze.
AGP zawiera punkty rozszerzeń dla wtyczek, które umożliwiają kontrolowanie danych wejściowych kompilacji i rozszerzanie jej funkcjonalności za pomocą nowych kroków, które można zintegrować ze standardowymi zadaniami kompilacji. Poprzednie wersje wtyczki Androida do obsługi Gradle nie miały oficjalnych interfejsów API wyraźnie oddzielonych od implementacji wewnętrznych. Od wersji 7.0 wtyczka AGP ma zestaw oficjalnych, stabilnych interfejsów API, na których możesz polegać.
Cykl życia interfejsu API AGP
AGP określa stan swoich interfejsów API zgodnie z cyklem życia funkcji Gradle:
- Wewnętrzne: nie są przeznaczone do użytku publicznego.
- W fazie inkubacji: dostępne do użytku publicznego, ale nieostateczne, co oznacza, że mogą nie być zgodne wstecznie w ostatecznej wersji.
- Public: stabilne i dostępne do użytku publicznego.
- Wycofane: nie są już obsługiwane i zostały zastąpione nowymi interfejsami API.
Zasady wycofywania (funkcji lub usług)
Wtyczka AGP ewoluuje – stare interfejsy API są wycofywane i zastępowane nowymi, stabilnymi interfejsami API oraz nowym językiem DSL (Domain Specific Language). Ta zmiana będzie obejmować kilka wersji AGP. Więcej informacji znajdziesz w harmonogramie migracji interfejsu API/DSL AGP.
Gdy interfejsy API AGP zostaną wycofane (w ramach tej migracji lub w inny sposób), będą nadal dostępne w bieżącej wersji głównej, ale będą generować ostrzeżenia. Wycofane interfejsy API zostaną całkowicie usunięte z AGP w kolejnej wersji głównej. Jeśli na przykład interfejs API zostanie wycofany we wtyczce Androida do obsługi Gradle 7.0, będzie on dostępny w tej wersji i będzie generować ostrzeżenia. Ten interfejs API nie będzie już dostępny w AGP 8.0.
Przykłady nowych interfejsów API używanych w typowych dostosowaniach kompilacji znajdziesz w przepisach na wtyczkę Androida do obsługi Gradle. Zawierają przykłady typowych dostosowań kompilacji. Więcej informacji o nowych interfejsach API znajdziesz też w naszej dokumentacji.
Kompilowanie z użyciem artefaktu interfejsu AGP API
Aby napisać niestandardową wtyczkę Gradle lub logikę kompilacji (np. w katalogu buildSrc, module wtyczki konwencji lub samodzielnym projekcie wtyczki), która rozszerza AGP, skompiluj ją z artefaktem gradle-api AGP zamiast pełnego artefaktu implementacji wtyczki.
Dlaczego warto kompilować z artefaktem gradle-api?
AGP publikuje 2 główne artefakty:
com.android.tools.build:gradle-api: zawiera oficjalne interfejsy API AGP, w tym stabilne interfejsy API (takie jakAndroidComponentsExtension,Varianti publiczne interfejsy DSL), interfejsy API w fazie inkubacji (oznaczone jako@Incubating) i wycofane interfejsy API (oznaczone jako@Deprecated).com.android.tools.build:gradle: zawiera pełną implementację środowiska wykonawczego AGP, w tym klasy wewnętrzne, zadania kompilacji i integracje kompilatora.
Kompilowanie z użyciem artefaktu gradle-api zapewnia kilka kluczowych korzyści:
- Unikanie wewnętrznych interfejsów API: dzięki temu kod wtyczki będzie miał dostęp tylko do oficjalnych interfejsów API. Zapobiega to przypadkowym zależnościom od wewnętrznych klas implementacji (
com.android.build.gradle.internal.*), które mogą ulec zmianie lub zostać usunięte w różnych wersjach AGP bez ostrzeżenia. - Szybsze kompilacje i mniejszy rozmiar: artefakt
gradle-apima znacznie mniejsze drzewo zależności niż pełny artefaktgradle. Zmniejsza to rozmiary pobieranych plików, zapobiega zanieczyszczeniu ścieżki klasy i przyspiesza kompilację logiki kompilacji. - Izolacja środowiska wykonawczego: wtyczki niestandardowe wymagają definicji interfejsu API tylko podczas kompilacji. W czasie działania pełna implementacja AGP jest dostarczana przez kompilację Androida, gdy stosowana jest wtyczka aplikacji na Androida lub biblioteki.
Dodaj zależność
Dodaj artefakt gradle-api jako zależność w pliku kompilacji projektu wtyczki lub w katalogu buildSrc. Artefakt gradle-api jest opublikowany w repozytorium Maven Google. Aby rozwiązać problem z artefaktem, upewnij się, że kompilacja zawiera repozytorium google(). Możesz na przykład skonfigurować go w bloku dependencyResolutionManagement.repositories pliku settings.gradle lub settings.gradle.kts albo w bloku repositories pliku kompilacji wtyczki lub pliku kompilacji katalogu buildSrc.
Z katalogiem wersji
Jeśli Twój projekt korzysta z katalogu wersji, zdefiniuj zależność gradle-api w pliku gradle/libs.versions.toml:
[versions]
androidGradlePlugin = "9.4.0"
[libraries]
android-gradle-plugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" }
Alias katalogu android-gradle-plugin-api generuje akcesor zabezpieczeń wpisywania libs.android.gradle.plugin.api.
Następnie dodaj libs.android.gradle.plugin.api do bloku dependencies w pliku kompilacji wtyczki:
Kotlin
dependencies { compileOnly(libs.android.gradle.plugin.api) }
Dynamiczny
dependencies { compileOnly libs.android.gradle.plugin.api }
Bez katalogu wersji
Jeśli Twój projekt nie korzysta z katalogu wersji, zadeklaruj zależność bezpośrednio w pliku kompilacji wtyczki:
Kotlin
dependencies { compileOnly("com.android.tools.build:gradle-api:9.4.0") }
Dynamiczny
dependencies { compileOnly 'com.android.tools.build:gradle-api:9.4.0' }
Użyj opcji konfiguracji compileOnly jako domyślnej dla samodzielnych wtyczek przeznaczonych do publikacji. Projekt, który korzysta z wtyczki, stosuje AGP i udostępnia klasy środowiska wykonawczego w ścieżce klasy buildscript. Użycie konfiguracji compileOnly zapobiega wyciekowi zależności środowiska wykonawczego w opublikowanej wtyczce w określonej wersji AGP i pozwala uniknąć konfliktów wersji u użytkowników. Jeśli tworzysz wewnętrzną logikę kompilacji, np. wtyczki w katalogu buildSrc lub wtyczki konwencji w kompilacjach złożonych, użyj opcji konfiguracji implementation. Te wtyczki działają w ramach kompilacji i wymagają AGP w ścieżce klasy środowiska wykonawczego.
Podstawy kompilacji Gradle
Ten przewodnik nie obejmuje całego systemu kompilacji Gradle. Obejmuje on jednak minimalny niezbędny zestaw pojęć, które pomogą Ci przeprowadzić integrację z naszymi interfejsami API, oraz zawiera linki do głównej dokumentacji Gradle, w której znajdziesz więcej informacji.
Zakładamy, że masz podstawową wiedzę o tym, jak działa Gradle, w tym jak konfigurować projekty, edytować pliki kompilacji, stosować wtyczki i uruchamiać zadania. Aby poznać podstawy Gradle w odniesieniu do AGP, zapoznaj się z artykułem Konfigurowanie kompilacji. Informacje o ogólnych zasadach dostosowywania wtyczek Gradle znajdziesz w artykule Developing Custom Gradle Plugins (Tworzenie niestandardowych wtyczek Gradle).
Słowniczek typów leniwych w Gradle
Gradle oferuje wiele typów, które zachowują się „leniwie” lub pomagają odroczyć ciężkie obliczenia lub tworzenie Task na późniejsze etapy kompilacji. Te typy stanowią podstawę wielu interfejsów API Gradle i AGP. Poniższa lista zawiera główne typy Gradle związane z wykonywaniem leniwym oraz ich kluczowe metody.
Provider<T>- Zapewnia wartość typu
T(gdzie „T” oznacza dowolny typ), którą można odczytać podczas fazy wykonywania za pomocąget()lub przekształcić w nowy typProvider<S>(gdzie „S” oznacza inny typ) za pomocą metodmap(),flatMap()izip(). Pamiętaj, że metodaget()nie powinna być wywoływana podczas fazy konfiguracji.map(): akceptuje lambdę i generujeProvidertypuS,Provider<S>. Argument lambda funkcjimap()przyjmuje wartośćTi zwraca wartośćS. Wyrażenie lambda nie jest wykonywane natychmiast. Zamiast tego jego wykonanie jest odroczone do momentu wywołania funkcjiget()na wynikowym obiekcieProvider<S>, co sprawia, że cały łańcuch jest leniwy.flatMap(): również akceptuje funkcję lambda i zwracaProvider<S>, ale funkcja lambda przyjmuje wartośćTi zwracaProvider<S>(zamiast zwracać bezpośrednio wartośćS). Użyj flatMap(), gdy nie można określić S w czasie konfiguracji i można uzyskać tylkoProvider<S>. W praktyce, jeśli użyjeszmap()i otrzymasz wynik typuProvider<Provider<S>>, prawdopodobnie oznacza to, że lepiej było użyćflatMap().zip(): umożliwia połączenie 2 instancjiProviderw celu utworzenia nowej instancjiProviderz wartością obliczoną za pomocą funkcji, która łączy wartości z 2 wejściowych instancjiProviders.
Property<T>- Implementuje
Provider<T>, więc udostępnia też wartość typuT. W odróżnieniu od właściwościProvider<T>, która jest tylko do odczytu, możesz też ustawić wartość właściwościProperty<T>. Możesz to zrobić na 2 sposoby:- Ustaw wartość typu
Tbezpośrednio, gdy jest dostępna, bez konieczności odroczonych obliczeń. - Ustaw inny element
Provider<T>jako źródło wartości elementuProperty<T>. W tym przypadku wartośćTjest materializowana tylko wtedy, gdy wywoływana jest funkcjaProperty.get().
- Ustaw wartość typu
TaskProvider- Implementuje
Provider<Task>. Aby wygenerowaćTaskProvider, użyjtasks.register(), a nietasks.create(). Dzięki temu zadania będą tworzone tylko wtedy, gdy będą potrzebne. Możesz użyćflatMap(), aby uzyskać dostęp do danych wyjściowychTask, zanimTaskzostanie utworzony. Może to być przydatne, jeśli chcesz użyć danych wyjściowych jako danych wejściowych dla innych instancjiTask.
Dostawcy i ich metody przekształcania są niezbędne do konfigurowania danych wejściowych i wyjściowych zadań w sposób leniwy, czyli bez konieczności tworzenia wszystkich zadań z góry i rozwiązywania wartości.
Dostawcy zawierają też informacje o zależnościach zadań. Gdy tworzysz Provider, przekształcając dane wyjściowe Task, ten Task staje się niejawną zależnością Provider i będzie tworzony i uruchamiany za każdym razem, gdy wartość Provider zostanie rozwiązana, np. gdy inny Task będzie tego wymagać.
Oto przykład rejestrowania 2 zadań, GitVersionTask i ManifestProducerTask, z odroczeniem tworzenia instancji Task do momentu, gdy będą one rzeczywiście potrzebne. Wartość wejściowa ManifestProducerTask jest ustawiona na Provider uzyskaną z danych wyjściowych GitVersionTask, więc ManifestProducerTask pośrednio zależy od GitVersionTask.
// Register a task lazily to get its TaskProvider.
val gitVersionProvider: TaskProvider =
project.tasks.register("gitVersionProvider", GitVersionTask::class.java) {
it.gitVersionOutputFile.set(
File(project.buildDir, "intermediates/gitVersionProvider/output")
)
}
...
/**
* Register another task in the configuration block (also executed lazily,
* only if the task is required).
*/
val manifestProducer =
project.tasks.register(variant.name + "ManifestProducer", ManifestProducerTask::class.java) {
/**
* Connect this task's input (gitInfoFile) to the output of
* gitVersionProvider.
*/
it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
}
Te 2 zadania zostaną wykonane tylko wtedy, gdy zostaną wyraźnie wywołane. Może się to zdarzyć w ramach wywołania Gradle, na przykład jeśli uruchomisz ./gradlew
debugManifestProducer lub jeśli dane wyjściowe ManifestProducerTask są połączone z innym zadaniem i jego wartość jest wymagana.
Chociaż będziesz pisać zadania niestandardowe, które wykorzystują dane wejściowe lub generują dane wyjściowe, AGP nie oferuje bezpośredniego publicznego dostępu do własnych zadań. Są one szczegółem implementacji, który może się zmieniać w zależności od wersji. Zamiast tego AGP udostępnia interfejs Variant API i dostęp do danych wyjściowych zadań, czyli artefaktów kompilacji, które można odczytywać i przekształcać. Więcej informacji znajdziesz w sekcji Variant API, artefakty i zadania w tym dokumencie.
Fazy kompilacji Gradle
Kompilowanie projektu jest z natury skomplikowanym i wymagającym zasobów procesem. Istnieją różne funkcje, takie jak unikanie konfiguracji zadań, sprawdzanie aktualności i buforowanie konfiguracji, które pomagają zminimalizować czas poświęcany na powtarzalne lub niepotrzebne obliczenia.
Aby zastosować niektóre z tych optymalizacji, skrypty i wtyczki Gradle muszą przestrzegać rygorystycznych reguł podczas każdej z poszczególnych faz kompilacji Gradle: inicjowania, konfiguracji i wykonywania. W tym przewodniku skupimy się na fazach konfiguracji i wykonania. Więcej informacji o wszystkich fazach znajdziesz w przewodniku po cyklu życia kompilacji Gradle.
Etap konfiguracji
Podczas fazy konfiguracji oceniane są skrypty kompilacji wszystkich projektów, które są częścią kompilacji, stosowane są wtyczki i rozwiązywane są zależności kompilacji. Ta faza powinna służyć do konfigurowania kompilacji za pomocą obiektów DSL oraz do rejestrowania zadań i ich danych wejściowych w sposób leniwy.
Ponieważ faza konfiguracji jest zawsze uruchamiana, niezależnie od tego, które zadanie ma zostać wykonane, szczególnie ważne jest, aby była ona jak najprostsza i aby żadne obliczenia nie zależały od danych wejściowych innych niż same skrypty kompilacji.
Oznacza to, że nie należy uruchamiać programów zewnętrznych, odczytywać danych z sieci ani wykonywać długich obliczeń, które można odłożyć do fazy wykonywania jako instancje Task.
Faza wykonania
W fazie wykonywania są wykonywane żądane zadania i zadania od nich zależne. W szczególności wykonywane są metody klasy Task oznaczone jako @TaskAction. Podczas wykonywania zadania możesz odczytywać dane wejściowe (np. pliki) i rozwiązywać leniwe dostawcy, wywołując Provider<T>.get(). Rozwiązanie dostawców lazy w ten sposób uruchamia sekwencję wywołań map() lub flatMap(), które są zgodne z informacjami o zależnościach zadań zawartymi w dostawcy. Zadania są uruchamiane leniwie, aby zmaterializować wymagane wartości.
Variant API, artefakty i zadania
Variant API to mechanizm rozszerzeń we wtyczce Androida do obsługi Gradle, który umożliwia manipulowanie różnymi opcjami, zwykle ustawianymi za pomocą DSL w plikach konfiguracji kompilacji, które wpływają na kompilację Androida. Interfejs Variant API zapewnia też dostęp do pośrednich i końcowych artefaktów utworzonych przez kompilację, takich jak pliki klas, manifest łączony czy pliki APK/AAB.
Proces kompilacji i punkty rozszerzenia na Androidzie
Podczas interakcji z AGP używaj specjalnie utworzonych punktów rozszerzeń zamiast rejestrować typowe wywołania zwrotne cyklu życia Gradle (takie jak afterEvaluate()) lub konfigurować jawne zależności Task. Zadania utworzone przez AGP są traktowane jako szczegóły implementacji i nie są udostępniane jako publiczny interfejs API. Nie próbuj pobierać instancji obiektów Task ani odgadywać nazw Task i dodawać wywołań zwrotnych lub zależności bezpośrednio do tych obiektów Task.
AGP wykonuje te czynności, aby utworzyć i wykonać instancje Task, które z kolei generują artefakty kompilacji. Po głównych krokach tworzenia obiektu Variant następują wywołania zwrotne, które umożliwiają wprowadzanie zmian w niektórych obiektach utworzonych w ramach kompilacji. Ważne jest, aby pamiętać, że wszystkie wywołania zwrotne mają miejsce podczas fazy konfiguracji (opisanej na tej stronie) i muszą być wykonywane szybko, a wszelkie skomplikowane zadania powinny być odkładane do odpowiednich instancji Task podczas fazy wykonywania.
- Analiza DSL: w tym momencie skrypty kompilacji są oceniane, a różne właściwości obiektów DSL Androida z bloku
androidsą tworzone i ustawiane. Podczas tej fazy rejestrowane są też wywołania zwrotne interfejsu Variant API opisane w kolejnych sekcjach. finalizeDsl(): wywołanie zwrotne, które umożliwia zmianę obiektów DSL przed ich zablokowaniem na potrzeby tworzenia komponentu (wariantu). ObiektyVariantBuildersą tworzone na podstawie danych zawartych w obiektach DSL.Blokowanie DSL: DSL jest teraz zablokowany i nie można już wprowadzać zmian.
beforeVariants(): to wywołanie zwrotne może wpływać na to, które komponenty są tworzone, i na niektóre ich właściwości za pomocąVariantBuilder. Nadal umożliwia modyfikowanie procesu kompilacji i generowanych artefaktów.Tworzenie wariantów: lista komponentów i artefaktów, które zostaną utworzone, jest teraz sfinalizowana i nie można jej zmienić.
onVariants(): w tym wywołaniu zwrotnym uzyskujesz dostęp do utworzonych obiektówVarianti możesz ustawić wartości lub dostawców dla zawartych w nich wartościProperty, które mają być obliczane leniwie.Blokowanie wariantów: obiekty wariantów są teraz zablokowane i nie można już wprowadzać zmian.
Utworzone zadania: obiekty
Varianti ich wartościPropertysą używane do tworzenia instancjiTask, które są niezbędne do wykonania kompilacji.
AGP wprowadza interfejs AndroidComponentsExtension, który umożliwia rejestrowanie wywołań zwrotnych dla finalizeDsl(), beforeVariants() i onVariants().
Rozszerzenie jest dostępne w skryptach kompilacji w bloku androidComponents:
// This is used only for configuring the Android build through DSL.
android { ... }
// The androidComponents block is separate from the DSL.
androidComponents {
finalizeDsl { extension ->
...
}
}
Zalecamy jednak zachowanie skryptów kompilacji tylko na potrzeby konfiguracji deklaratywnej przy użyciu DSL bloku Androida i przeniesienie niestandardowej logiki imperatywnej do katalogu buildSrc lub wtyczek zewnętrznych. Możesz też zapoznać się z przykładowymi buildSrc w naszym repozytorium GitHub z przepisami Gradle, aby dowiedzieć się, jak utworzyć wtyczkę w projekcie. Upewnij się, że wtyczka kompiluje się z artefaktem gradle-api w pliku kompilacji. Oto przykład rejestrowania wywołań zwrotnych z kodu wtyczki:
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
Przyjrzyjmy się bliżej dostępnym wywołaniom zwrotnym i przypadkom użycia, które wtyczka może obsługiwać w każdym z nich:
finalizeDsl(callback: (DslExtensionT) -> Unit)
W tym wywołaniu zwrotnym możesz uzyskać dostęp do obiektów DSL utworzonych przez przeanalizowanie informacji z bloku android w plikach kompilacji i zmodyfikować je.
Te obiekty DSL będą używane do inicjowania i konfigurowania wariantów w późniejszych fazach kompilacji. Możesz na przykład automatycznie tworzyć nowe konfiguracje lub zastępować właściwości, ale pamiętaj, że wszystkie wartości muszą zostać rozwiązane w momencie konfiguracji, więc nie mogą zależeć od żadnych danych zewnętrznych.
Po zakończeniu wykonywania tego wywołania zwrotnego obiekty DSL nie są już przydatne i nie należy już przechowywać odwołań do nich ani modyfikować ich wartości.
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
extension.buildTypes.create("extra").let {
it.isJniDebuggable = true
}
}
}
}
beforeVariants()
Na tym etapie tworzenia masz dostęp do obiektów VariantBuilder, które określają warianty do utworzenia i ich właściwości. Możesz na przykład automatycznie wyłączyć określone warianty i ich testy lub zmienić wartość właściwości (np. minSdk) tylko w wybranym wariancie. Podobnie jak w przypadku finalizeDsl() wszystkie podane wartości muszą zostać rozwiązane w czasie konfiguracji i nie mogą zależeć od danych zewnętrznych. Po zakończeniu wykonywania wywołania zwrotnego beforeVariants() nie wolno modyfikować obiektów VariantBuilder.
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
Wywołanie zwrotne beforeVariants() może opcjonalnie przyjmować obiekt VariantSelector, który można uzyskać za pomocą metody selector() w androidComponentsExtension. Możesz go używać do filtrowania komponentów uczestniczących w wywołaniu zwrotnym na podstawie ich nazwy, typu kompilacji lub wariantu usługi.
androidComponents {
beforeVariants(selector().withName("adfree")) { variantBuilder ->
variantBuilder.minSdk = 23
}
}
onVariants()
Zanim zostanie wywołana metoda onVariants(), wszystkie artefakty, które zostaną utworzone przez AGP, są już określone, więc nie można ich już wyłączyć. Możesz jednak zmodyfikować niektóre wartości używane w zadaniach, ustawiając je dla atrybutów Property w obiektach Variant. Wartości Property zostaną rozwiązane dopiero po wykonaniu zadań wtyczki Androida do obsługi Gradle, więc możesz je bezpiecznie połączyć z dostawcami z własnych zadań niestandardowych, które będą wykonywać wymagane obliczenia, w tym odczytywać dane z zewnętrznych źródeł, takich jak pliki lub sieć.
// onVariants also supports VariantSelectors:
onVariants(selector().withBuildType("release")) { variant ->
// Gather the output when we are in single mode (no multi-apk).
val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }
// Create version code generating task
val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
it.outputFile.set(project.layout.buildDirectory.file("${variant.name}/versionCode.txt"))
}
/**
* Wire 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.map { it.outputFile.get().asFile.readText().toInt() })
}
Dodawanie wygenerowanych źródeł do kompilacji
Wtyczka może generować kilka typów źródeł, takich jak:
- kod aplikacji w katalogu
java, - Zasoby Androida w katalogu
res - Zasoby Java w katalogu
resources - Zasoby na Androida w katalogu
assets
Pełną listę źródeł, które możesz dodać, znajdziesz w interfejsie Sources API.
Ten fragment kodu pokazuje, jak dodać niestandardowy folder źródłowy o nazwie ${variant.name} do zbioru źródeł Java za pomocą funkcji addStaticSourceDirectory(). Następnie ten folder jest przetwarzany przez zestaw narzędzi Androida.
onVariants { variant ->
variant.sources.java?.let { java ->
java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
}
}
Więcej informacji znajdziesz w przepisie addJavaSource.
Ten fragment kodu pokazuje, jak dodać katalog z zasobami Androida wygenerowanymi z niestandardowego zadania do zbioru źródeł res. W przypadku innych typów źródeł proces jest podobny.
onVariants(selector().withBuildType("release")) { variant ->
// Step 1. Register the task.
val resCreationTask =
project.tasks.register<ResCreatorTask>("create${variant.name}Res")
// Step 2. Register the task output to the variant-generated source directory.
variant.sources.res?.addGeneratedSourceDirectory(
resCreationTask,
ResCreatorTask::outputDirectory)
}
...
// Step 3. Define the task.
abstract class ResCreatorTask: DefaultTask() {
@get:OutputFiles
abstract val outputDirectory: DirectoryProperty
@TaskAction
fun taskAction() {
// Step 4. Generate your resources.
...
}
}
Więcej informacji znajdziesz w przepisie addCustomAsset.
Uzyskiwanie dostępu do artefaktów i ich modyfikowanie
Oprócz możliwości modyfikowania prostych właściwości obiektów Variant AGP zawiera też mechanizm rozszerzeń, który umożliwia odczytywanie i przekształcanie pośrednich i końcowych artefaktów utworzonych podczas kompilacji. Możesz na przykład odczytać ostateczną, scaloną zawartość pliku AndroidManifest.xml w niestandardowym Task, aby ją przeanalizować, lub całkowicie zastąpić ją zawartością pliku manifestu wygenerowanego przez niestandardowy Task.
Listę obecnie obsługiwanych artefaktów znajdziesz w dokumentacji referencyjnej klasy Artifact. Każdy typ artefaktu ma pewne właściwości, które warto znać:
Moc zbioru
Moc zbioru Artifact reprezentuje liczbę instancji FileSystemLocation lub liczbę plików bądź katalogów typu artefaktu. Informacje o liczbie elementów artefaktu możesz uzyskać, sprawdzając jego klasę nadrzędną: artefakty z pojedynczym elementem FileSystemLocation będą podklasą Artifact.Single, a artefakty z wieloma instancjami FileSystemLocation będą podklasą Artifact.Multiple.
Typ: FileSystemLocation
Możesz sprawdzić, czy Artifact reprezentuje pliki czy katalogi, patrząc na jego sparametryzowany typ FileSystemLocation, który może być RegularFile lub Directory.
Obsługiwane operacje
Każda klasa Artifact może implementować dowolny z tych interfejsów, aby wskazać, które operacje obsługuje:
Transformable: umożliwia użycieArtifactjako danych wejściowych dlaTask, który wykonuje na nim dowolne przekształcenia i generuje nową wersjęArtifact.Appendable: dotyczy tylko artefaktów, które są podklasamiArtifact.Multiple. Oznacza to, że doArtifactmożna dołączać elementy, czyli niestandardowyTaskmoże tworzyć nowe instancje tego typuArtifact, które zostaną dodane do istniejącej listy.Replaceable: dotyczy tylko artefaktów, które są podklasamiArtifact.Single. Zastępowalny elementArtifactmożna zastąpić zupełnie nową instancją, która jest wynikiem działaniaTask.
Oprócz 3 operacji modyfikujących artefakt każdy artefakt obsługuje operację get() (lub getAll()), która zwraca Provider z ostateczną wersją artefaktu (po zakończeniu wszystkich operacji na nim).
Wiele wtyczek może dodawać dowolną liczbę operacji na artefaktach do potoku z wywołania zwrotnego onVariants(), a AGP zapewni ich prawidłowe połączenie, dzięki czemu wszystkie zadania będą uruchamiane we właściwym czasie, a artefakty będą prawidłowo tworzone i aktualizowane. Oznacza to, że gdy operacja zmienia dane wyjściowe przez ich dołączenie, zastąpienie lub przekształcenie, następna operacja zobaczy zaktualizowaną wersję tych artefaktów jako dane wejściowe itd.
Punktem wejścia do rejestrowania operacji jest klasa Artifacts.
Poniższy fragment kodu pokazuje, jak uzyskać dostęp do instancji Artifacts z właściwości obiektu Variant w wywołaniu zwrotnym onVariants().
Następnie możesz przekazać niestandardowy obiekt TaskProvider, aby uzyskać obiekt TaskBasedOperation (1), i użyć go do połączenia wejść i wyjść za pomocą jednej z metod wiredWith* (2).
Dokładna metoda, którą musisz wybrać, zależy od mocy zbioru i typu FileSystemLocation implementowanego przez Artifact, który chcesz przekształcić.
Na koniec przekazujesz typ Artifact do metody reprezentującej wybraną operację na obiekcie *OperationRequest, który otrzymujesz w odpowiedzi, np. toAppendTo(), toTransform() lub toCreate() (3).
androidComponents.onVariants { variant ->
val manifestUpdater = // Custom task that will be used for the transform.
project.tasks.register(variant.name + "ManifestUpdater", ManifestTransformerTask::class.java) {
it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
}
// (1) Register the TaskProvider w.
val variant.artifacts.use(manifestUpdater)
// (2) Connect the input and output files.
.wiredWithFiles(
ManifestTransformerTask::mergedManifest,
ManifestTransformerTask::updatedManifest)
// (3) Indicate the artifact and operation type.
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
W tym przykładzie MERGED_MANIFEST to SingleArtifact, a
to RegularFile. Z tego powodu musimy użyć metody wiredWithFiles, która akceptuje pojedyncze odwołanie RegularFileProperty dla danych wejściowych i pojedyncze odwołanie RegularFileProperty dla danych wyjściowych. W klasie TaskBasedOperation są inne metody wiredWith*, które będą działać w przypadku innych kombinacji liczby elementów Artifact i typów FileSystemLocation.
Aby dowiedzieć się więcej o rozszerzaniu AGP, przeczytaj te sekcje instrukcji systemu kompilacji Gradle:
- Developing Custom Gradle Plugins
- Implementowanie wtyczek Gradle
- Tworzenie niestandardowych typów zadań Gradle
- Konfiguracja leniwa
- Unikanie konfiguracji zadań