Как я могу реализовать универсальную фабрику ViewModel для обеспечения всех моделей ViewModel моего проекта? Чтобы было ясно, мои ViewModels имеют зависимости (как параметры конструктора)
Ну, есть один, который называется GithubБраузер, но это не учебник, а проект. Вы должны знать кинжал для Android, чтобы сделать это. Или вы можете проверить код ниже:
@Singleton
class DaggerViewModelFactory @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)
}
}
}
Эта часть создаст «универсальную» модель представления для всего вашего приложения. Таким образом, ViewModel
создается с назначенными аргументами. После этого вам нужно внедрить фабричный модуль в свои модули Singleton и включить его в компонент.
@Component(
modules = [... ViewModelModule::class]
)
interface AppCompoenent{}
Теперь самое интересное:
@Suppress("unused")
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(MyViewModel::class)
abstract fun bindsMyViewModel(viewModel: MyViewModel): ViewModel
@Binds
abstract fun bindsViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
}
Поскольку кинжал поддерживает множественное связывание, вы можете связывать его сколько угодно ViewModels
по своему усмотрению.
Ключ модели представления:
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Вы в основном помещаете значения в хэш-карту. Эти ценности — ваши ViewModel
.
Хит строить! Готово. После этого вы просто вставляете ViewModelProvider.Facory
в свой фрагмент. Чем в вашем ViewModel
вы можете сделать:
class MyViewModel @Inject constructor(
private val dependency: YourDependency
) : ViewModel() {}
Чтобы было понятно, что вы просили в комментариях. Во-первых, нет особой необходимости знать, что происходит внутри DaggerViewModelFactory
, хотя я не рекомендую учиться таким образом, потому что я большой поклонник принципа «Всегда знать, что происходит». К вашему сведению, DaggerViewModelFactory
— это просто класс, который принимает Map
с каждым классом, который расширяет ViewModel
в качестве ключа, и зависимости этого класса в качестве значения. При использовании Provider<T>
Dagger знает, как найти эти зависимости, но еще не доводит их до вас, пока вы не вызовете provider.get()
. Думайте об этом как о ленивой инициализации.
Теперь проверьте modelClass.isAssignableFrom(it.key)
. Он просто проверяет, действительно ли этот класс расширяется ViewModel
.
Что касается вашего второго вопроса, важно понять первую часть. Поскольку Dagger
поддерживает множественное связывание, это означает, что вы можете предоставлять зависимости с помощью Map<Key, Value>
. Например, Map<HomeViewModel, Provider<ViewModel>>
в основном скажет кинжалу, который даст мне зависимости HomeViewModel
. Кинжал скажет: Как узнать, какие HomeViewModel
зависимости? И вы отвечаете: я уже определил для этого ключ, и это сам класс HomeViewModel
. Итак, вы просто создаете аннотацию, комбинируете ее с @Binds
и @IntoMap
, а на фоне Dagger просто выполняет map.put(HomeViewModel::class, AndDependencies)
.
А VIewModelKey нужен? Я не знаю, что такое области действия Target и Retention. Может быть, это как модуль для отображения ViewModels?
Не волнуйтесь, я добавлю в пост дополнительные исследования.
сделано, пожалуйста, проверьте еще раз
Большое спасибо! Ясно и легко понять!
Рад, что помог :)
Я только начинаю работать с Dagger в проекте, и мне трудно понять, многие вещи я просто копировал и вставлял, не зная, что они делают, вы порекомендуете мне какой-либо способ изучить это?
Ну, это своего рода продвинутый, когда вы новичок. У меня есть собственная статья о кинжале, если вы хотите изучить основы: medium.com/@stavro96/keep-it-simple-with-dagger-2-241d32e14de
Отлично, пойду проверю!
Я видел ваш пример на Medium, но я не понимаю, как используются области действия, область с аннотацией Retention говорит только о том, что зависимость должна запускаться, когда приложение запущено, поэтому не имеет смысла или, по крайней мере, я не понимаю видеть это.
Масштаб — это просто метка, ничего больше. Так же, как переопределение. это ничего не делает. Единственное, что он делает, это чтобы dagger знал уровень зависимости. Если вы проверите аннотацию @Singleton
, вы найдете то же самое. Итак, масштабы определяют иерархию найма. Пожалуйста, посмотрите видео скрученного уравнения на прицелах для лучшего понимания.
Где я могу найти это видео?
В конце моей статьи у вас есть 3 ресурса для изучения кинжала. Откройте скрученные уравнения и перейдите к серии прицелов.
извините, это название Dagger 2 Android Tutorial
Я это вижу и теперь понимаю, еще раз спасибо!
Но есть одна мысль, которую я не вижу ясно. Я знаю, для чего нужны области, но, например, область действия MapKey отображает мою активность, так что это не просто отметка о том, что я знаю уровень зависимости, нет?
@MapKey
по своему названию не выглядит размахом
Последний вопрос, пожалуйста, чтобы закончить понимать все: Для чего нужен ViewModelProvider.Factory?
Поскольку ViewModel
имеет собственный жизненный цикл, вы не можете инициализировать ViewModel
с помощью обычного конструктора, поэтому для него вам нужна фабрика.
Я публикую новый вопрос, если вы можете помочь мне решить проблему @coroutineDispatcher stackoverflow.com/questions/57206369/…
Должен ли я знать, как сделать ModelViewFactory или достаточно скопировать-вставить?