Dependency Injection – Koin – Teoria

Nadszedł czas na praktykę. Opowiem Ci o bibliotece służącej do wstrzykiwania zależności (ang. Dependency Injection). Mowa tu o bibliotece Koin.

Wstrzykiwanie zależności (eng. Depedency Injection) niezależnie od sposobu implementacji jest podstawowym krokiem przy budowaniu aplikacji. Szersze omówienie DI znajdziesz w moim wcześniejszym artykule. Teraz skupimy się na bibliotece Koin, która w bardzo prosty sposób pozwoli wdrożyć Ci ten paradygmat programowania do Twoich aplikacji. Poniżej znajdziesz plan niniejszego wpisu. Zapraszam! 🙂

  1. „Moduł” – podstawowy komponent DI
  2. Deklarowanie zależności
  3. Wstrzykiwanie zależności
  4. Rejestracja i uruchomienie Koina

„Moduł” – podstawowy komponent DI

Moduł w bibliotece Koin to komponent, w którym deklarujemy wszystkie zależności (eng. dependencies) jakie chcemy potem wstrzykiwać. W naszej aplikacji, jesteśmy w stanie zadeklarować dowolną liczbę modułów. Dlatego gorąco zachęcam byś tworzył moduły, które w logiczny sposób pogrupują znajdujące się w niej zależności.
Przykładowo, jeden moduł może być poświęcony bazie danych, inny networkingowi, jeszcze inny klasom używanym w obrębie całej aplikacji… i tak dalej.

Tworzenie komponentu: „Module”

Więc, jak utworzyć nasz pierwszy moduł? Bardzo prosto:

  1. By móc korzystać z biblioteki Koin dodaj tą zależność do pliku build.gradle: implementation „org.koin:koin-androidx-viewmodel: 2.0.1”
  2. Tworzymy oddzielny plik w naszym projekcie i definiujemy zmienną, która będzie przechowywać referencję do naszego modułu (tej zmiennej użyjemy nieco później)
  3. Wywołujemy funkcję module(), która zdefiniuje nasz moduł
val appModule = module {
// our dependencies
}

Jak widzisz na powyższym przykładzie, do zmiennej appModule został przypisany nowy moduł. Teraz przejdźmy do wypełnienia naszego modułu czyli zadeklarowania zależności.

Deklarowanie zależności

Nasze zależności możemy zadeklarować za pomocą specjalnych funkcji:

  1. factory
  2. single
  3. viewModel – o tym nieco później

Factory – dla zwyczajnej instancji

Kiedy użyjemy metody factory, instancje klas będą tworzone na nowo podczas każdorazowego wstrzyknięcia. Mówiąc w skrócie, to tak jakbyśmy za każdym razem wywoływali konstruktor do stworzenia obiektu.

Single – dla pojedynczej instancji

Jak możesz spodziewać się po samej nazwie, użycie funkcji single będzie skutkować utworzeniem jedynej instancji danej klasy. Zadeklarowany w ten sposób obiekt będzie singletonem.

ViewModel – nowe udogodnienie

Koin oferuje także niezwykle użyteczną rzecz – integrację z komponentem ViewModel. By skorzystać z tego udogodnienia, wystarczy że użyjemy słowa kluczowego viewModel.

Teraz, zobacz jak wygląda to na przykładzie:

val appModule = module {

// single instance of something
     single {
        SampleSingleClass()
     }
     // factory instance of something
     factory {
        SampleFactoryClass()
     }
     // instance of our ViewModel
     viewModel { SampleViewModel() }


     // "named" to recognize instances of the same type
     single(named("red")) {
        BicycleClass(color = "red")
     }

     single(named("blue")) {
        BicycleClass(color = "blue")
     }

     // "get" provided WheelClass into CarClass
     single {
        WheelClass()
     }
     factory {
        CarClass(wheelClass = get())
     }
}

Pobieranie zależności

Jak już mogłeś zauważyć, na powyższym przykładzie użyliśmy funkcji get . Służy ona do pobrania odpowiedniej zależności i umieszczenia jej w miejscu użycia.

Wstrzykiwanie zależności

Zależności możemy wstrzykiwać na różne sposoby. Między innymi poprzez konstruktor:

// CarClass & WheelClass are declared in the appModule
class CarClass(
   private val wheelClass: WheelClass
) {

  fun prepareCar() {
     // WheelClass is ready to use
     wheelClass.mount()
  }
}

Zależność możemy także wstrzyknąć poprzez „leniwą” inicjalizację za pomocą funkcji by inject() lub poprzez bezpośrednie wstrzyknięcie do instancji, funkcją get() (jest to inna funkcja niż ta przedstawiona przy okazji tworzenia modułu).

// Inject in a simple Activity
class SampleActivity() : AppCompatActivity() {

    // lazy inject SampleFactoryClass into property
    private val factoryClass: SampleFactoryClass by inject()

    //lazy inject ViewModel
    val sampleViewModel: SampleViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // or directly get any instance
        val sampleFactoryClass: SampleFactoryClass = get()

        //inject by directly getting ViewModel instance
        val viewModel: SampleViewModel = getViewModel()
    }
}

Teraz, na podstawie powyższego przykładu, opowiem Ci co różni get() od by inject():

  • by inject() – wstrzyknięcie nastąpi dopiero w momencie gdy będziemy chcieli użyć SampleFactoryClass, na przykład wywołując jej metodę. Z tego powodu to wstrzyknięcie nazywany jest leniwym.
  • get() lub getViewModel() – wstrzyknięcie jest natychmiastowe. Zależność zostaje natychmiast pobrana z kontenera.

Rejestracja i uruchomienie Koina

W celu zarejestrowania i uruchomienia Koina, zastosujemy funkcję startKoin. Pamiętaj, że musi zostać wywołana z poziomu Application.

class SampleApp : Application() {
   ovveride fun onCreate() {
      super.onCreate()

      startKoin {
         androidLogger()
         androidContext(this@SampleApp)
         modules(
            listOf(appModule)
         )
       }
   }
}

Teraz objaśnię Ci co znajduje się wewnątrz funkcji startKoin:

  1. androidLogger – używamy jej jeśli chcemy, by w konsoli wyświetlały się logi z biblioteki (Koina)
  2. androidContext – przekazujemy context aplikacji do kontenera Koina.
  3. modules – wskazujemy, które moduły mają zostać użyte do tworzenia drzewa zależności.

Zakończenie

To było by na tyle z teorii o bibliotece Koin… ALE! Czy pamiętasz naszą aplikację, którą skrupulatnie rozwijamy? W kolejnym artykule ulepszymy ją o nowo poznaną bibliotekę 🙂 Gorąco zachęcam do poczytania !!!

A gdybyś nie chciał przeoczyć zbliżającego się artykułu, kliknij na poniższy link i zapisz się na darmowy, merytoryczny mailing. Otrzymasz także SUPER NIESPODZIANKĘ! Nie zawiedziesz się 😀

Dobrego dnia! Cześć !!!