Подготовка Dagger 2 через Модули против Инжектора - PullRequest
1 голос
/ 21 января 2020

Я пытаюсь добавить dagger- android к проекту Kotlin и запутался, когда он требуется для создания модуля и когда достаточно просто объявить конструктор Inject.

Предположим, что есть следующий график зависимостей:

Activity
    -> ViewModel
        -> Repository
            -> Webservice
            -> Dao
                -> Database
                    -> Application

Чтобы обеспечить ViewModel для Activity, мы создаем соответствующие модули для действия и фабрики ViewModel, а затем вручную создаем ViewModel в Activity следующим образом:

@Module
abstract class ActivityModule {
    @ContributesAndroidInjector
    abstract fun mainActivity(): MainActivity
}

// Skiping ViewModelKey and ViewModelFactory code for brevity
@Module
abstract class ViewModelModule {
    @Binds
    internal abstract fun bindViewModelFactory(
        factory: ViewModelFactory
    ): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    internal abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
}

class MainActivity : DaggerAppCompatActivity() {
    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        viewModel = ViewModelProviders.of(this, viewModelFactory)
            .get(HomeViewModel::class.java)
    }
}

Чтобы предоставить Repository для ViewModel, мы просто объявляем @Inject constructor следующим образом:

class MainViewModel @Inject constructor(private val repository: Repository): ViewModel() {}

Чтобы предоставить Webservice и Dao для Repository как а также Database для Дао мы создаем соответствующие Модули следующим образом:

@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideWebservice() = Webservice.create()
}

interface Webservice {
    ...
    companion object Factory {
        fun create(): Webservice {
            ...
            return retrofit
        }
    }
}


@Module
class DataModule {
    @Provides
    @Singleton
    fun provideApplicationDatabase(app: Application) =
        AppDatabase.getDatabase(app)

    @Provides
    @Singleton
    fun provideUserDao(db: AppDatabase) = db.userDao()
}

@Database(...)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        fun getDatabase(context: Context): AppDatabase {
            ...
            return instance
        }
    }
}

И Application предоставляется для Dabatabase некоторыми волхвами c в AppComponent и в классе Application

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    NetworkModule::class,
    DataModule::class,
    ViewModelModule::class,
    ActivityModule::class
])
interface AppComponent: AndroidInjector<App> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun create(application: Application): Builder
        fun build(): AppComponent
    }
}

class App : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
        DaggerAppComponent.builder().create(this).build()
}

Вопросы :

  1. Как база данных получает экземпляр приложения? Это AndroidSupportInjectionModule, который делает magi c?
  2. Почему нам нужно создавать модули для веб-сервиса и базы данных, а не для репозитория? Можно ли аннотировать интерфейс Webservice и сам класс Database, чтобы пропустить создание для них отдельных модулей кинжала?

1 Ответ

2 голосов
/ 23 января 2020

Вопрос 1 Как база данных получает экземпляр приложения? Это AndroidSupportInjectionModule, который делает волхвы c?

Ответ: Нет, это не работа AndroidSupportInjectionModule. AndroidSupportInjectionModule включен в поддержку Dagger Android, которая помогает в

«Настройка привязок для обеспечения удобства использования классов кинжалов. android и dagger. android .support.» Найдено здесь

Итак, в основном, вы просто передаете контекст приложения при создании Dagger Builder, просто передаваете его из класса приложения сейчас. Получив его в главном компоненте, у вас есть приложение. контекст во всех ваших модулях, и нам нужен контекст, когда мы инициализируем Room Database.

Вопрос 2 Почему нам нужно создавать модули для веб-сервиса и базы данных, а не для репозитория? Можно ли аннотировать интерфейс Webservice и сам класс Database, чтобы пропустить создание для них отдельных модулей кинжала?

Ответ: Во-первых, всегда старайтесь добиться конструктора Injection. Общая идея модулей - «Если мы не владеем классом, мы не можем @annotate его конструктор, поэтому мы создаем модули для обеспечения их реализации». Также, если мы хотим внедрить Interface, мы можем достичь его через класс реализации с помощью конструктора Injection. Таким образом, мы не владеем инициализацией WebService и Database, поэтому мы создаем их Модули и предоставляем их, чтобы мы могли получить их экземпляры в наших репозиториях. У нас есть свой класс репозитория, поэтому мы можем внедрить их через конструктор Injection.

...