Wstrzykiwanie zależności na Androidzie

Wstrzykiwanie zależności (DI) to technika powszechnie wykorzystywana w programowaniu dostosowane do programowania na Androida. Stosując się do zasad DI, z myślą o dobrej architekturze aplikacji.

Wdrożenie wstrzykiwania zależności przynosi takie korzyści:

  • Ponowne wykorzystanie kodu
  • Łatwość refaktoryzacji
  • Łatwość testowania

Podstawowe informacje o wstrzykiwaniu zależności

Zanim omówię wstrzykiwanie zależności na Androidzie, na tej stronie znajdziesz ogólne omówienie sposobu wstrzykiwania zależności.

Co to jest wstrzykiwanie zależności?

Zajęcia często wymagają odwołań do innych klas. Na przykład klasa Car może potrzebować odwołania do klasy Engine. Te wymagane klasy to tzw. dependencies. W tym przykładzie klasa Car jest zależna od w którym należy uruchomić instancję klasy Engine.

Klasa może uzyskać obiekt na 3 sposoby:

  1. Klasa tworzy potrzebną zależność. W przykładzie powyżej Car utworzy i zainicjuje własną instancję Engine
  2. Pobrać je gdzieś indziej. Niektóre interfejsy API Androida, takie jak Pobieraj Context i getSystemService(). Pracuj nad tym sposób.
  3. Podaj ją jako parametr. Aplikacja może udostępniać te funkcje: podczas tworzenia klasy lub przekazać je do funkcji które wymagają każdej zależności. W przykładzie powyżej Car konstruktor otrzymałby parametr Engine.

Trzecia opcja to wstrzykiwanie zależności. Przy takim podejściu zależności klasy i zapewnić je, zamiast mieć klasę do Twojej instancji.

Oto przykład. Bez wstrzykiwania zależności, reprezentujący obiekt Car, który tworzy własną zależność Engine w kodzie. Wygląda to tak:

Kotlin

class Car {

    private val engine = Engine()

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.start()
}

Java

class Car {

    private Engine engine = new Engine();

    public void start() {
        engine.start();
    }
}


class MyApp {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
    }
}
Klasa samochodu bez wstrzykiwania zależności

To nie jest przykład wstrzykiwania zależności, ponieważ klasa Car to tworząc własny element Engine. Może to powodować problemy, ponieważ:

  • Car i Engine są ściśle sprzężone – instancja Car korzysta z jednego typu Engine. Nie można łatwo zastosować żadnych podklas ani alternatywnych implementacji . Jeśli Car ma utworzyć własny element Engine, musisz utworzyć 2 typy Car zamiast ponownego użycia tego samego pola Car w wyszukiwarkach danego typu Gas i Electric.

  • Mocna zależność od Engine utrudnia testowanie. Komponent Car używa rzeczywiste wystąpienie Engine, uniemożliwiając użycie testuj podwójnie, aby zmodyfikować Engine w różnych przypadkach testowych.

Jak wygląda kod po wstrzykiwaniu zależności? Zamiast każdej instancji Car podczas tworzenia własnego obiektu Engine przy inicjowaniu, otrzymuje Engine jako parametr w swoim konstruktorze:

Kotlin

class Car(private val engine: Engine) {
    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val engine = Engine()
    val car = Car(engine)
    car.start()
}

Java

class Car {

    private final Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}


class MyApp {
    public static void main(String[] args) {
        Engine engine = new Engine();
        Car car = new Car(engine);
        car.start();
    }
}
Klasa samochodu używająca wstrzykiwania zależności

Funkcja main używa Car. Działanie Car zależy od funkcji Engine, więc aplikacja tworzy wystąpienia Engine, a następnie używa go do utworzenia wystąpienia Car. zalety tej metody opartej na DI:

  • Możliwość ponownego wykorzystania elementu Car. Możesz przekazywać różne implementacje interfejsu Engine do Car Możesz na przykład zdefiniować nową podklasę klasy Engine o nazwie ElectricEngine, których Car ma używać. Jeśli używasz DI, wystarczy, że w instancji zaktualizowanej podklasy ElectricEngine, a Car nadal działa. bez dalszych zmian.

  • Łatwe testowanie aplikacji Car. Możesz zdać test podwójnej precyzji, aby sprawdzić różne w różnych sytuacjach. Można na przykład utworzyć duplikat testowy Engine o nazwie FakeEngine i skonfigurować je pod kątem różnych testów.

Wstrzykiwanie zależności w Androidzie można przeprowadzić na 2 główne sposoby:

  • Constructor Injection (Wstrzykiwanie przez konstruktora). W ten sposób został opisany powyżej. Musisz zdać zależności klasy do jej konstruktora.

  • Field Injection (Wstrzykiwanie). określonych zajęć na platformie Android; takie jak działania i fragmenty są tworzone przez system, więc konstruktor wstrzykiwanie jest niemożliwe. Po wstrzykiwaniu pól tworzenie zależności jest tworzone po utworzeniu zajęć. Kod będzie wyglądał tak:

Kotlin

class Car {
    lateinit var engine: Engine

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.engine = Engine()
    car.start()
}

Java

class Car {

    private Engine engine;

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}

class MyApp {
    public static void main(String[] args) {
        Car car = new Car();
        car.setEngine(new Engine());
        car.start();
    }
}

Automatyczne wstrzykiwanie zależności

W poprzednim przykładzie udało Ci się utworzyć i podać zależności oraz nimi zarządzać w różnych klasach – bez konieczności korzystania z biblioteki. Jest to tzw. ręczne wstrzykiwanie zależności lub ręczne wstrzykiwanie zależności. W: Car na przykład zaistniała tylko jedna zależność, ale więcej zależności i klas ręczne wstawianie zależności staje się bardziej męczące. Ręczne wstrzykiwanie zależności Wiąże się też z kilkoma problemami:

  • W przypadku dużych aplikacji łączenie wszystkich zależności i ich łączenie ale mogą wymagać dużego, stałego kodu. W wielowarstwowym architektura, aby utworzyć obiekt dla górnej warstwy, ze wszystkimi zależnościami warstwymi leżącymi poniżej. Oto konkretny przykład: prawdziwy samochód może potrzebować silnika, skrzyni biegów, podwozia i innych części. a silnik potrzebuje cylindrów i świec zapłonowych.

  • Jeśli nie możesz skonstruować zależności przed ich przekazaniem, na przykład możesz na przykład używać leniwych inicjacji lub zawężać zakres obiektów do przepływów aplikacji – musisz utworzyć i utrzymywać niestandardowy kontener (lub wykres zależności), który zarządza okresami istnienia zależności w pamięci.

Istnieją biblioteki, które pozwalają rozwiązać ten problem przez automatyzację procesu tworzenia i dostarczania zależności. Można je podzielić na 2 kategorie:

  • Rozwiązania oparte na odczuciach, które łączą zależności w czasie działania.

  • rozwiązania statyczne, które generują kod do łączenia zależności. podczas kompilowania danych.

Dagger to popularna biblioteka do wstrzykiwania zależności dla Javy. Kotlin i Android obsługiwanego przez Google. Sztylet ułatwia korzystanie z DI w aplikacji, tworząc wykres zależności i zarządzając nim. it zapewnia w pełni statyczne i czasowe kompilowanie zależności, które obejmują problemów z rozwojem i wydajnością rozwiązań opartych na refleksji, takich jak: Guice

Alternatywy dla wstrzykiwania zależności

Alternatywą dla wstrzykiwania zależności jest użycie lokalizatora usług. Wzorzec projektowy lokalizatora usług usprawnia usuwanie powiązania klas od konkretnych zależności. Tworzysz zajęcia (nazywany lokalizatorem usług), który tworzy i przechowuje zależności, a następnie zapewnia te zależności na żądanie.

Kotlin

object ServiceLocator {
    fun getEngine(): Engine = Engine()
}

class Car {
    private val engine = ServiceLocator.getEngine()

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.start()
}

Java

class ServiceLocator {

    private static ServiceLocator instance = null;

    private ServiceLocator() {}

    public static ServiceLocator getInstance() {
        if (instance == null) {
            synchronized(ServiceLocator.class) {
                instance = new ServiceLocator();
            }
        }
        return instance;
    }

    public Engine getEngine() {
        return new Engine();
    }
}

class Car {

    private Engine engine = ServiceLocator.getInstance().getEngine();

    public void start() {
        engine.start();
    }
}

class MyApp {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
    }
}

Wzorzec lokalizatora usług różni się od sposobu wstrzykiwania zależności i konsumpcji pierwiastków. Dzięki wzorcowi lokalizatora usług klasy mają kontrolować i prosić o wstrzykiwanie obiektów; wstrzykiwania zależności, aplikacja ma kontrolę i proaktywnie wstrzykiwa wymagane obiekty.

W porównaniu z wstrzykiwaniem zależności:

  • Zbiór zależności wymaganych przez lokalizator usług tworzy kod jest trudniejsze do przeprowadzenia, ponieważ wszystkie testy muszą współdziałać z tym samym globalnym lokalizator usług.

  • Zależności są kodowane w implementacji klas, a nie w interfejsie API. W związku z tym trudniej jest określić z zewnątrz, czego potrzebuje dana klasa. W związku z tym zmiany Car lub zależności dostępne w lokalizatorze usług mogą spowodować uruchomienie lub test przez spowodowanie niepowodzenia odwołań.

  • Zarządzanie cyklem życia obiektów jest trudniejsze, jeśli chcesz zawęzić zakres do poza czasem trwania aplikacji.

Korzystanie z Hilt w aplikacji na Androida

Hilt (Hilt) to plecak zalecany przez Jetpacka. do wstrzykiwania zależności w Androidzie. Hilt określa standardowy sposób działania DI w aplikacji, udostępniając kontenery dla każdej klasy Androida w projekty i automatyczne zarządzanie ich cyklami życia.

Hilt opiera się na popularnej bibliotece DI szkoła, aby skorzystać z kompilowanie poprawności czasu, wydajności środowiska wykonawczego, skalowalności i Android Studio z Daggera.

Więcej informacji o Hilt znajdziesz na stronie Dependency Injection with Hilt:

Podsumowanie

Wstrzykiwanie zależności zapewnia aplikacji te zalety:

  • Ponowne wykorzystanie klas i odłączanie zależności: łatwiejsza wymiana implementacji zależności. Możliwość ponownego wykorzystania kodu jest lepsza dzięki odwróceniu a klasy nie kontrolują już sposobu tworzenia zależności, ale możesz pracować z dowolną konfiguracją.

  • Łatwość refaktoryzacji: zależności stają się możliwą do zweryfikowania częścią interfejsu API więc można je sprawdzić w momencie utworzenia obiektu lub podczas kompilacji. a nie jako szczegóły implementacji.

  • Łatwość testowania: klasa nie zarządza zależnościami, więc jeśli możesz go przetestować, przekazać na różne sposoby, by przetestować wszystkie w różnych przypadkach.

Aby w pełni zrozumieć zalety wstrzykiwania zależności, wypróbuj je ręcznie w aplikacji, jak opisano w sekcji Ręczne wstrzykiwanie zależności.

Dodatkowe materiały

Aby dowiedzieć się więcej o wstrzykiwaniu zależności, znajdziesz w tych dodatkowych materiałach poniżej.

Próbki