Как ограничить сферу с dagger2 о ViewModelFactory? - PullRequest
0 голосов
/ 10 июня 2019

Я использую компонент архитектуры Android с Dagger2, который рекомендован Google. Я следовал за Google Sample , но он не идеален.

Это решение упрощает внедрение, но если мои ViewModels полагаются на базу данных, Dao был необходим при создании viewmodel. На данный момент фабрика была привязана к AppModule для обеспечения глобального использования. Поэтому ViewModel должен быть предоставлен в AppModule для добавления на карту. Таким образом, Дао также будет глобальным.

Поскольку Дао может использоваться только в какой-то специальной деятельности, его создание в глобальном масштабе является пустой тратой и неудобно в управлении.

Я попытался переместить определение функции bindViewModel в ActivityModule, но ошибки были даны следующим образом:

 [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent {
                ^
      java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          com.chenhe.platform.viewmodel.MyViewModelFactory(creators)
      com.chenhe.platform.viewmodel.MyViewModelFactory is injected at
......

AppModule:

@Module(subcomponents = [LocalWatchFaceComponent::class], includes = [ViewModelModule::class])
class AppModule(private val context: Context) {

    @Provides
    @Singleton
    fun provideContext(): Context = context

    @Provides
    @Singleton
    fun provideAppDataBase(): AppDatabase = AppDatabase.getInstance(context)

    // I want move code below to ActivityModule. But Dao was needed by viewmodel by factory map
    @Provides
    fun provideLocalWatchFaceDao(database: AppDatabase) = database.localWatchFaceDao()
}

ViewModel:

class LocalWatchFaceViewModel @Inject constructor(
        private val appCtx: Context,
        localWatchFaceRepository: LocalWatchFaceRepository) : ViewModel() {}

MapKey:

@MustBeDocumented
@Target(
        AnnotationTarget.FUNCTION,
        AnnotationTarget.PROPERTY_GETTER,
        AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

ViewModelModule:

@Module
abstract class ViewModelModule {
    @Binds
    abstract fun bindViewModelFactory(factory: MyViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(LocalWatchFaceViewModel::class)
    abstract fun bindLocalWatchFaceViewModel(viewModel: LocalWatchFaceViewModel): ViewModel
}

Factory:

@Singleton
class MyViewModelFactory @Inject constructor(
        private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

Есть ли план изящно разделить область?

...