Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a viewmodel factory is needed in Android?

We have been discussing about this but we don't know the reason of creating a viewmodel factory to create a viewmodel instead of instantiate the viewmodel directly. What is the gain of creating a factory that just creates the viewmodel?

I just put a simple example of how I did it without Factory

here is the kodein module:

val heroesRepositoryModel = Kodein {     bind<HeroesRepository>() with singleton {         HeroesRepository()     }      bind<ApiDataSource>() with singleton {         DataModule.create()     }      bind<MainViewModel>() with provider {         MainViewModel()     } } 

The piece of the Activity where I instantiate the viewmodel without using the factory

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()     } 

The ViewModel where I instantiate the usecase directly without having it in the constructor

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()     } } 

So here a example using factory

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)     } 

The 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")     } } 

And the viewmodel:

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()     } 

Here is the complete first example:

https://github.com/ibanarriolaIT/Marvel/tree/mvvm

And the complete second example:

https://github.com/AdrianMeizoso/Payment-App

like image 741
Iban Arriola Avatar asked Jan 29 '19 10:01

Iban Arriola


People also ask

What is the purpose of ViewModel factory?

Factory Method Pattern is the way to let ViewModel wait for value to initialize or another logic that programmer want and then we pass value into ViewModel on Initialization part. ViewModelFactory uses factory to create objects while Factory method is a method that return the copy of the same class.

Why do we need ViewModel in Android?

The purpose of ViewModel is to encapsulate the data for a UI controller to let the data survive configuration changes. For information about how to load, persist, and manage data across configuration changes, see Saving UI States.

When should I create a ViewModel?

The ViewModel exists from when the you first request a ViewModel (usually in the onCreate the Activity) until the Activity is finished and destroyed. onCreate may be called several times during the life of an Activity, such as when the app is rotated, but the ViewModel survives throughout.


1 Answers

We can not create ViewModel on our own. We need ViewModelProviders utility provided by Android to create ViewModels.

But ViewModelProviders can only instantiate ViewModels with no arg constructor.

So if I have a ViewModel with multiple arguments, then I need to use a Factory that I can pass to ViewModelProviders to use when an instance of MyViewModel is required.

For example -

public class MyViewModel extends ViewModel {     private final MyRepo myrepo;     public MyViewModel(MyRepo myrepo) {          this.myrepo = myrepo;     } } 

To instantiate this ViewModel, I need to have a factory which ViewModelProviders can use to create its instance.

ViewModelProviders Utility can not create instance of a ViewModel with argument constructor because it does not know how and what objects to pass in the constructor.

like image 56
Vishal Arora Avatar answered Oct 21 '22 13:10

Vishal Arora