Вы находитесь на странице: 1из 6

Продолжаем продвинутый курс по созданию приложения “Топ-100 криптовалют” на языке

Kotlin. На этом уроке мы реализуем работу с Dependency injection.

Dependency injection
Dependency Injection (DI) — это набор паттернов и принципов разработки програмного
обеспечения, которые позволяют писать слабосвязный код. Внедрение зависимостей — это
стиль настройки объекта, при котором поля объекта задаются внешней сущностью. Это
позволяет избежать сильных связей между объектами в коде, которые затрудняют
масштабирование, тестирование и поддержку проекта. Больше теории о внедрении
зависимостей можно почитать здесь.

Для внедрения зависимостей в нашем проекте мы будем использовать популярную


библиотеку Dagger 2. Ее использование обеспечит нам удобный доступ к компонентам
приложения и простую настройку сложных зависимостей. Подключите библиотеку в проект в
файле сборки build.gradle (app):

1 ext.daggerVersion = '2.11'
2
3 dependencies {
4 …
5 //dagger
6     implementation "com.google.dagger:dagger:$daggerVersion"
7     kapt "com.google.dagger:dagger-compiler:$daggerVersion"
8 }
9
Не забудьте также в верхней части файла сборки build.gradle (app) добавить такую строку:

1 apply plugin: 'kotlin-kapt'

Создаем подпапку di в главном пакете и в ней создаем класс приложения App,


унаследованный от Application:

1 package info.fandroid.top100currencies.di
2
3 import android.app.Application
4
5 class App : Application() {
6
7 override fun onCreate() {
8 super.onCreate()
9 }
10 }
11

Переопределите в классе функцию onCreate(). Здесь мы будем инициализировать


компоненты библиотеки Dagger 2.

Чтобы определить этот класс как класс приложения, нужно в файле манифеста прописать
имя класса App в параметре имени секции application:

1 <application
2 android:name=".di.App"
3 …
4

Библиотека Dagger 2 использует в своей работе аннотации:

@Inject – базовая аннотация, с помощью которой запрашивается зависимость

@Module – классы, функции которых предоставляют зависимости

@Provides – функции внутри @Module, предоставляющие зависимости

@Component – связующее звено между @Inject и @Module


Начнем с создания в подпапке di классов, предоставляющих зависимости. Класс AppModule
будет предоставлять контекст для доступа к ресурсам в любой части приложения:

1 package info.fandroid.topcrypts.di
2
3 import android.content.Context
4 import dagger.Module
5 import dagger.Provides
6 import javax.inject.Singleton
7
8 @Module
9 class AppModule(private val app: App) {
10
11 @Provides
12 @Singleton
13 fun provideContext(): Context = app
14
15 }
16

Класс помечен аннотацией @Module, сообщающей Даггеру, что функции этого класса
предоставляют зависимости. Функция provideContext() помечена аннотацией @Provides как
раз для этой цели.

Аннотация @Singletone означает, что Даггер при инициализации компонента создаст


единственный экземпляр помеченной зависимости, то есть синглтон. И при каждом
затребовании данной зависимости будет предоставлять этот единственный экземпляр. Это
позволит избежать ненужного пересоздания объектов, утечек памяти и других проблем.

Аналогично создайте еще несколько модулей. Модуль ChartModule будет использоваться


для работы с графиком, пока оставьте его пустым:

1 package info.fandroid.top100currencies.di
2
3 import dagger.Module
4
5 @Module
6 class ChartModule {
7 }
8

Модуль MvpModule аналогично:

1 package info.fandroid.top100currencies.di
2
3 import dagger.Module
4
5 @Module
6 class MvpModule {
7 }
8
Модуль RestModule содержит функции для предоставления зависимостей Gson,
OkHttpClient, Retrofit и CoinGeckoApi:

1 package info.fandroid.top100currencies.di
2
3 import com.google.gson.Gson
4 import com.google.gson.GsonBuilder
5 import dagger.Module
6 import dagger.Provides
7 import info.fandroid.topcrypts.rest.CoinGeckoApi
8 import okhttp3.OkHttpClient
9 import okhttp3.logging.HttpLoggingInterceptor
10 import retrofit2.Retrofit
11 import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
12 import retrofit2.converter.gson.GsonConverterFactory
13 import java.util.concurrent.TimeUnit
14 import javax.inject.Named
15 import javax.inject.Singleton
16
17 @Module
18 class RestModule {
19
20 @Provides
21 @Singleton
22 fun provideGson(): Gson =
23 GsonBuilder()
24 .setLenient()
25 .create()
26
27 @Provides
28 @Singleton
29 fun provideOkHttpClient(): OkHttpClient =
30 OkHttpClient.Builder()
31
32 .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
33 .connectTimeout(60, TimeUnit.SECONDS)
34 .build()
35
36
37 @Provides
38 @Singleton
39 @Named("COINGECKO_API")
40 fun provideGeckoRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit =
41 Retrofit.Builder()
42 .baseUrl("https://api.coingecko.com/api/v3/")
43 .addConverterFactory(GsonConverterFactory.create(gson))
44 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
45 .client(okHttpClient)
46 .build()
47
48
49 @Provides
50 @Singleton
51 fun provideGeckoApiService(@Named("COINGECKO_API") retrofit: Retrofit): CoinGeckoApi =
52 retrofit.create(CoinGeckoApi::class.java)
53 }

Функция provideGson() создает конвертер для сериализации данных типа Gson.

Функция provideOkHttpClient() создает клиент OkHttpClient, используемый библиотекой


Retrofit для HTTP-запросов.

Функция provideGeckoRetrofit() создает Retrofit клиент, передает базовый адрес запроса,


определяет Gson как конвертер данных, получаемых от сервера. Retrofit также может быть
расширен адаптерами для взаимодействия с другими библиотеками, такими как RxJava, с
помощью вызова метода addCallAdapterFactory билдера. При использовании адаптера
RxJava2CallAdapterFactory интерфейсы Retrofit могут возвращать типы RxJava 2.x,
например, Observable, Flowable или Single и т. д.   

Функция provideGeckoApiService() предоставляет подготовленный объект CoinGeckoApi.

Теперь создайте интерфейс AppComponent с аннотацией @Component и списком модулей в


качестве ее параметра:

1 package info.fandroid.top100currencies.di
2
3 import dagger.Component
4 import info.fandroid.top100currencies.activities.MainActivity
5 import javax.inject.Singleton
6
7 @Component(modules = arrayOf(AppModule::class, RestModule::class, MvpModule::class,
8 ChartModule::class))
9 @Singleton
10 interface AppComponent {
11
12 fun inject(mainActivity: MainActivity)
13
14
15 }
Данной аннотацией мы говорим Даггеру, что AppComponent содержит четыре модуля:
AppModule, ChartModule, MvpModule и RestModule. Зависимости, которые провайдит
каждый из этих модулей, доступны для всех остальных модулей, объединенных в
компоненте AppComponent.

Пока здесь только одна функция inject(mainActivity: MainActivity), которая сообщает Даггеру
класс, в который мы хотим внедрять зависимости. В дальнейшем мы добавим здесь и
другие классы.

Наконец, инициализируем Даггер в классе App:

1 package info.fandroid.top100currencies.di
2
3 import android.app.Application
4
5 class App : Application() {
6
7 companion object {
8 lateinit var appComponent: AppComponent
9 }
10
11 override fun onCreate() {
12 super.onCreate()
13 initializeDagger()
14 }
15
16 private fun initializeDagger() {
17 appComponent = DaggerAppComponent.builder()
18 .appModule(AppModule(this))
19 .restModule(RestModule())
20 .mvpModule(MvpModule())
21 .chartModule(ChartModule())
22 .build()
23 }
24 }
25

Метод builder() класса App. DaggerAppComponent до компиляции не доступен, и среда


разработки будет сообщать, что класса DaggerAppComponent не существует. Также не
будет подсказок при построении билдера, поэтому инициализацию AppComponent с
помощью DaggerAppComponent придется писать «вслепую» первый раз. После написания
сделайте ребилд проекта.

Вам также может понравиться