Зачем нужна фабрика Viewmodel в Android?
Мы обсуждали это, но мы не знаем причину создания фабрики моделей представления для создания модели представления вместо непосредственного создания экземпляра модели представления. Какова выгода от создания фабрики, которая просто создает модель представления?
Я просто привел простой пример того, как я это сделал без Factory
вот модуль кодеин:
val heroesRepositoryModel = Kodein {
bind<HeroesRepository>() with singleton {
HeroesRepository()
}
bind<ApiDataSource>() with singleton {
DataModule.create()
}
bind<MainViewModel>() with provider {
MainViewModel()
}
}
Часть Activity, где я создаю экземпляр модели представления без использования фабрики
class MainActivity : AppCompatActivity() {
private lateinit var heroesAdapter: HeroAdapter
private lateinit var viewModel: MainViewModel
private val heroesList = mutableListOf<Heroes.MapHero>()
private var page = 0
private var progressBarUpdated = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this)
.get(MainViewModel::class.java)
initAdapter()
initObserver()
findHeroes()
}
ViewModel, где я создаю экземпляр usecase напрямую, не имея его в конструкторе
class MainViewModel : ViewModel(), CoroutineScope {
private val heroesRepository: HeroesRepository = heroesRepositoryModel.instance()
val data = MutableLiveData<List<Heroes.MapHero>>()
private var job: Job = Job()
override val coroutineContext: CoroutineContext
get() = uiContext + job
fun getHeroesFromRepository(page: Int) {
launch {
try {
val response = heroesRepository.getHeroes(page).await()
data.value = response.data.results.map { it.convertToMapHero() }
} catch (e: HttpException) {
data.value = null
} catch (e: Throwable) {
data.value = null
}
}
}
override fun onCleared() {
super.onCleared()
job.cancel()
}
}
Так вот пример использования фабрики
class ListFragment : Fragment(), KodeinAware, ContactsAdapter.OnContactListener {
override val kodein by closestKodein()
private lateinit var adapterContacts: ContactsAdapter
private val mainViewModelFactory: MainViewModelFactory by instance()
private val mainViewModel: MainViewModel by lazy {
activity?.run {
ViewModelProviders.of(this, mainViewModelFactory)
.get(MainViewModel::class.java)
} ?: throw Exception("Invalid Activity")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_list, container, false)
}
Viewmodelfactory:
class MainViewModelFactory (private val getContacts: GetContacts) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
return MainViewModel(getContacts) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
И модель представления:
class MainViewModel(private val getContacts: GetContacts) : BaseViewModel() {
lateinit var gamesList: LiveData<PagedList<Contact>>
var contactsSelectedData: MutableLiveData<List<Contact>> = MutableLiveData()
var contactsSelected: ArrayList<Contact> = ArrayList()
private val pagedListConfig by lazy {
PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(PAGES_CONTACTS_SIZE)
.setPageSize(PAGES_CONTACTS_SIZE)
.setPrefetchDistance(PAGES_CONTACTS_SIZE*2)
.build()
}
Вот полный первый пример:
https://github.com/ibanarriolaIT/Marvel/tree/mvvm
И полный второй пример:
5 ответов
Мы не можем создать ViewModel самостоятельно. Нам нужна утилита ViewModelProviders, предоставляемая Android для создания ViewModels.
Но ViewModelProviders может только создавать экземпляры ViewModels без конструктора arg.
Поэтому, если у меня есть ViewModel с несколькими аргументами, мне нужно использовать Factory, которую я могу передать ViewModelProviders для использования, когда требуется экземпляр MyViewModel.
Например -
public class MyViewModel extends ViewModel {
private final MyRepo myrepo;
public MyViewModel(MyRepo myrepo) {
this.myrepo = myrepo;
}
}
Чтобы создать экземпляр этой ViewModel, мне нужна фабрика, которую ViewModelProviders может использовать для создания своего экземпляра.
Утилита ViewModelProviders не может создать экземпляр ViewModel с помощью конструктора аргументов, поскольку она не знает, как и какие объекты передавать в конструктор.
Коротко,
если нам нужно передать некоторыеinput data
к constructor
из viewModel
, нам нужно создать factory class
для viewModel.
Как пример:-
class MyViewModelFactory constructor(private val repository: DataRepository): ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (modelClass.isAssignableFrom(MyViewModel::class.java!!)) {
MyViewModel(this.repository) as T
} else {
throw IllegalArgumentException("ViewModel Not Found")
}
}
}
Причина
Мы не можем напрямую создать объект ViewModel
поскольку он не знал бы оlifecyclerOwner
. Итак, мы используем:-
ViewModelProviders.of(this, MyViewModelFactory(repository)).get(MyViewModel::class.java)
Мы обсуждали это, но мы не знаем причину создания фабрики моделей представления для создания модели представления вместо непосредственного создания экземпляра модели представления. Какова выгода от создания фабрики, которая просто создает модель представления?
Потому что Android даст вам новый экземпляр, только если он еще не создан для данного конкретного LifecycleOwner.
Давайте также не будем забывать, что ViewModel сохраняются при изменениях конфигурации, поэтому, если вы поворачиваете телефон, вы не должны создавать новую ViewModel.
Если вы возвращаетесь к предыдущему действию и повторно открываете это действие, то предыдущая модель представления должна получить onCleared()
и новая активность должна иметь новую ViewModel.
Если вы не делаете это сами, вы, вероятно, должны просто доверять ViewModelProviders.Factory
делать свою работу.
(И вам нужна фабрика, потому что вы, как правило, не просто no-arg
конструктор, ваш ViewModel имеет аргументы конструктора, а ViewModelProvider
должен знать, как заполнить аргументы конструктора, когда вы используете конструктор не по умолчанию).
Когда мы просто используем ViewModel , мы не можем передавать аргументы этой ViewModel.
class GameViewModel() : ViewModel() {
init {
Log.d(TAG, "GameViewModel created")
}
}
Однако в некоторых случаях нам нужно передать свои собственные аргументы в ViewModel. Это можно сделать с помощью ViewModelFactory.
class ScoreViewModel(finalScore: Int) : ViewModel() {
val score = finalScore
init {
Log.d(TAG, "Final score: $finalScore")
}
}
И для создания экземпляра этой ViewModel нам нужен ViewModelProvider.Factory , так как простой ViewModel не может его создать.
class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
return ScoreViewModel(finalScore) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Когда дело доходит до создания экземпляра объекта этой ViewModel, то есть с ViewModelProvider, мы передаем ViewModelFactory в качестве аргумента, который содержит информацию о наших пользовательских аргументах, которые мы хотим передать. Это выглядит так:
viewModelFactory = ScoreViewModelFactory(score)
viewModel = ViewModelProvider(this,viewModelFactory).get(ScoreViewModel::class.java)
Вот почему существуют фабричные методы.
Мы можем передавать аргументы в viewmodel через фабрику viewmodel. Без factory мы не можем передавать аргументы в viewmodel.
class UserViewModelFactory(private val context: Context) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return UserViewModel(context) as T
}
}
class UserViewModel(private val context: Context) : ViewModel() {
private var listData = MutableLiveData<ArrayList<User>>()
init{
val userRepository : UserRepository by lazy {
UserRepository
}
if(context.isInternetAvailable()) {
listData = userRepository.getMutableLiveData(context)
}
}
fun getData() : MutableLiveData<ArrayList<User>>{
return listData
}
}
Вы можете вызвать viewmodel в действии, как показано ниже
val userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)
Для получения дополнительной информации: Пример Android MVVM Kotlin