Debugowanie błędów rozwiązywania zależności

Po dodaniu zależności mogą wystąpić problemy z zależnościami wymaganymi przez pierwotną zależność i konflikty między różnymi wersjami zależności. Poniżej znajdziesz informacje o tym, jak przeanalizować wykres zależności i rozwiązać typowe problemy.

Wskazówki dotyczące poprawiania błędów rozpoznawania zależności, które obejmują niestandardową logikę kompilacji, znajdziesz w artykule Niestandardowe strategie rozwiązywania zależności.

Wyświetl zależności modułu

Niektóre bezpośrednie zależności mogą też zależeć od siebie. Są to tzw. zależności przejściowe. Nie musisz ręcznie deklarować każdej zależności przejściowej, ale Gradle automatycznie je gromadzi i dodaje. Wtyczka Androida do obsługi Gradle udostępnia zadanie wyświetlające listę zależności obsługiwanych przez Gradle w przypadku danego modułu.

W przypadku każdego modułu raport grupuje też zależności na podstawie wariantu kompilacji, testowego zbioru źródłowego i ścieżki klasy. Poniżej znajduje się przykładowy raport dotyczący ścieżki klasy środowiska wykonawczego modułu aplikacji dla wariantu kompilacji do debugowania oraz ścieżki klasowej instrumentowanego zbioru źródłowego testowego.

debugRuntimeClasspath - Dependencies for runtime/packaging
+--- :mylibrary (variant: debug)
+--- com.google.android.material:material:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.2@aar
+--- androidx.constraintlayout:constraintlayout:1.1.3@aar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
...

debugAndroidTest
debugAndroidTestCompileClasspath - Dependencies for compilation
+--- androidx.test.ext:junit:1.1.0@aar
+--- androidx.test.espresso:espresso-core:3.1.1@aar
+--- androidx.test:runner:1.1.1@aar
+--- junit:junit:4.12@jar
...

Aby uruchomić zadanie, wykonaj te czynności:

  1. Wybierz Widok > Okna narzędzi > Gradle (lub kliknij Gradle na pasku narzędzi).
  2. Rozwiń AppName > Lista zadań > Android i kliknij dwukrotnie androidDependabilities. Gdy Gradle wykona zadanie, powinno zostać otwarte okno Uruchom, w którym wyświetlą się dane wyjściowe.

Aby dowiedzieć się więcej o zarządzaniu zależnościami w Gradle, zapoznaj się z podstawowymi informacjami o zarządzaniu zależnościami w przewodniku użytkownika Gradle.

Wyklucz zależności pośrednie

W miarę zwiększania zakresu aplikacja może zawierać szereg zależności, w tym zależności bezpośrednie i przechodnie (biblioteki, od których zależą zaimportowane biblioteki aplikacji). Aby wykluczyć zależności przejściowe, których już nie potrzebujesz, możesz użyć słowa kluczowego exclude w podany niżej sposób:

Kotlin

dependencies {
    implementation("some-library") {
        exclude(group = "com.example.imgtools", module = "native")
    }
}

Odlotowy

dependencies {
    implementation('some-library') {
        exclude group: 'com.example.imgtools', module: 'native'
    }
}

Wyklucz zależności pośrednie z konfiguracji testowych

Jeśli musisz wykluczyć z testów określone zależności przejściowe, przykładowy kod pokazany powyżej może nie działać zgodnie z oczekiwaniami. Dzieje się tak, ponieważ konfiguracja testowa (np. androidTestImplementation) rozszerzy konfigurację implementation modułu. Oznacza to, że zawsze zawiera zależności implementation, gdy Gradle rozwiązuje konfigurację.

Aby wykluczyć z testów zależności przejściowe, musisz to zrobić w czasie wykonywania w podany niżej sposób:

Kotlin

android.testVariants.all {
    compileConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
    runtimeConfiguration.exclude(group = "com.jakewharton.threetenabp", module = "threetenabp")
}

Odlotowy

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

Uwaga: nadal możesz używać słowa kluczowego exclude w bloku zależności, tak jak w pierwotnym przykładowym kodzie z sekcji Wyklucz zależności przechodnie, aby pominąć przechodnie zależności specyficzne dla konfiguracji testowej i nie zostały uwzględnione w innych konfiguracjach.

Naprawianie błędów związanych z rozwiązywaniem zależności

Gdy dodasz do projektu aplikacji wiele zależności, te zależności bezpośrednie i przejściowe mogą być ze sobą w konflikcie. Wtyczka Androida do obsługi Gradle próbuje płynnie rozwiązywać te konflikty, ale niektóre z nich mogą powodować błędy w czasie kompilacji lub działania.

Aby pomóc w ustaleniu, które zależności powodują błędy, sprawdź drzewo zależności aplikacji i wyszukaj zależności, które występują więcej niż raz lub występują w sprzecznych wersjach.

Jeśli nie możesz łatwo zidentyfikować zduplikowanej zależności, w interfejsie Androida Studio wyszukaj zależności, które zawierają zduplikowaną klasę, w następujący sposób:

  1. Na pasku menu wybierz Nawigacja > Zajęcia.
  2. W wyskakującym okienku wyszukiwania zaznacz pole Uwzględnij elementy spoza projektu.
  3. Wpisz nazwę klasy, która pojawi się w komunikacie o błędzie kompilacji.
  4. Sprawdź wyniki dotyczące zależności obejmujących klasę.

W sekcjach poniżej opisujemy różne typy błędów związanych z rozpoznawaniem zależności, które możesz napotkać, oraz sposoby ich naprawy.

Poprawianie błędów zduplikowanych zajęć

Jeśli klasa występuje więcej niż raz w ścieżce klasy w środowisku wykonawczym, wyświetli się błąd podobny do tego:

Program type already present com.example.MyClass

Ten błąd występuje zwykle z następujących przyczyn:

  • Zależność binarna obejmuje bibliotekę, którą Twoja aplikacja również jako zależność bezpośrednia. Na przykład aplikacja deklaruje bezpośrednią zależność od biblioteki A i biblioteki B, ale biblioteka A zawiera już bibliotekę B w swoim pliku binarnym.
    • Aby rozwiązać ten problem, usuń Bibliotekę B jako zależność bezpośrednią.
  • Twoja aplikacja ma lokalną zależność binarną i zdalną zależność binarną od tej samej biblioteki.
    • Aby rozwiązać ten problem, usuń jedną z zależności binarnej.

Rozwiązywanie konfliktów między ścieżkami klasy

Gdy Gradle rozwiązuje ścieżkę klasy kompilacji, najpierw rozpoznaje ścieżkę klasy runtime i używa jej wyniku do określenia, które wersje zależności powinny zostać dodane do ścieżki klasy kompilacji. Inaczej mówiąc, ścieżka klasy w środowisku wykonawczym określa wymagane numery wersji w przypadku identycznych zależności od ścieżek klas kolejnych.

Ścieżka klasy w czasie działania aplikacji określa też numery wersji, których Gradle wymaga do dopasowania zależności w ścieżce klasy środowiska wykonawczego do testowego pakietu APK aplikacji. Hierarchię ścieżek klas opisano na rys. 1.

Rysunek 1. Numery wersji zależności, które pojawiają się w wielu ścieżkach klas, muszą być zgodne zgodnie z tą hierarchią.

Konflikt, w którym różne wersje tej samej zależności występują w wielu ścieżkach klas, np. aplikacja zawiera wersję zależności za pomocą konfiguracji zależności implementation, a moduł biblioteki zawiera inną wersję zależności za pomocą konfiguracji runtimeOnly.

Podczas rozwiązywania zależności od środowiska wykonawczego i czasu kompilowania ścieżek klasowych w Androidzie wtyczka Gradle w wersji 3.3.0 lub nowszej próbuje automatycznie rozwiązywać niektóre konflikty wersji pobierania. Jeśli na przykład ścieżka klasy w środowisku wykonawczym zawiera bibliotekę A w wersji 2.0, a ścieżka klasyowa kompilacji zawiera bibliotekę A w wersji 1.0, wtyczka automatycznie zaktualizuje zależność od ścieżki klasy kompilowania do biblioteki A w wersji 2.0, aby uniknąć błędów.

Jeśli jednak ścieżka klasy w środowisku wykonawczym zawiera bibliotekę A w wersji 1.0, a ścieżka klasowa kompilacji z biblioteką A w wersji 2.0, wtyczka nie obniży zależności od ścieżki klasy kompilowania do wersji 1.0 biblioteki i nadal będzie wyświetlać się błąd podobny do tego:

Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.

Aby rozwiązać ten problem, wykonaj jedną z tych czynności:

  • Uwzględnij żądaną wersję zależności jako zależność api do modułu biblioteki. Oznacza to, że tylko moduł biblioteki deklaruje zależność, ale moduł aplikacji będzie miał też przechodni dostęp do swojego interfejsu API.
  • Możesz też zadeklarować zależność w obu modułach, ale upewnij się, że każdy z nich używa tej samej wersji zależności. Rozważ skonfigurowanie właściwości w całym projekcie, aby mieć pewność, że wersje każdej zależności są spójne w całym projekcie.