Why a viewmodel factory is needed in Android?

寵の児 提交于 2019-12-18 03:59:11

问题


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


回答1:


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.




回答2:


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?

Because Android will only give you a new instance if it's not yet created for that specific given LifecycleOwner.

Let's also not forget that ViewModels are kept alive across configuration changes, so if you rotate the phone, you're not supposed to create a new ViewModel.

If you are going back to a previous Activity and you re-open this Activity, then the previous ViewModel should receive onCleared() and the new Activity should have a new ViewModel.

Unless you're doing that yourself, you should probably just trust the ViewModelProviders.Factory to do its job.

(And you need the factory because you typically don't just have a no-arg constructor, your ViewModel has constructor arguments, and the ViewModelProvider must know how to fill out the constructor arguments when you're using a non-default constructor).



来源:https://stackoverflow.com/questions/54419236/why-a-viewmodel-factory-is-needed-in-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!