Если вы хотите, чтобы ваши view-модели были частью графа кинжалов, вам нужно сделать несколько вещей - используя мульти-привязки кинжала (только один раз, для более новых view-моделей это будет проще). Вы создадите новую фабрику viewmodel, которая позаботится о создании экземпляров viewmodels. Эта фабрика будет частью графика кинжала и, следовательно, будет иметь ссылки на все, что предоставляется через кинжал. Затем вы можете использовать инжектор конструктора через @Inject constructor(anyParameterFromDagger: Param)
или @Inject lateinit var someParam: Param
внутри тела viewmodel.
1) Создать классификатор для классов моделей представления
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
2) создать фабрику моделей представления, которая получает значения из мультибиндингов кинжала
@Singleton
class DaggerViewModelFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("unknown model class $modelClass")
}
try {
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
3) иметь модуль кинжала, который предоставит завод (из пункта 2), а затем ваши модели зрения
abstract class YourDaggerModuleWhichThenNeedToBePartOfYourGraphAsIncluded {
@Binds
abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory // this needs to be only one for whole app (therefore marked as `@Singleton`)
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
abstract fun bindMainActivityViewModel(vm: MainActivityViewModel): ViewModel // for every viewmodel you have in your app, you need to bind them to dagger
}
4) в своей деятельности, когда вы получаете свою модель представления, вам нужно использовать фабрику из кинжала: (места, помеченные как // TODO
в коде ниже)
class MainActivity : AppCompatActivity() {
@Inject
lateinit var tripsAdapter: TripsAdapter
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory // TODO this was added to the activity
override fun onCreate(savedInstanceState: Bundle?) {
// Inject external dependencies
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView();
setUpViewModel();
}
private fun setupRecyclerView() {
recycler_view.apply {
layoutManager = LinearLayoutManager(context)
adapter = tripsAdapter
}
}
private fun setUpViewModel(){
val model = ViewModelProviders.of(this, viewModelFactory)[MainActivityViewModel::class.java] // TODO this was changed
model.getTrips().observe(this, Observer { tripsAdapter.trips = it!! })
}
}
Я не предоставил код для включения модуля в компонент кинжала, так как я надеюсь, что это то, что вы уже сделали.
Вы можете узнать больше об этом, например. в этой средней статье (я не автор статьи):