Search code examples
javaandroidinheritanceandroid-architecture-componentsandroid-viewmodel

Overriding ViewModelStore


Is it possible to provide once own implementation of a ViewModelStore for ViewModelProviders to use instead of the default one?

More precisely, I'm interested in adding fun clear(vm: ViewModel) (or using an index or something similar) functionality to the ViewModelStore so that I can clear a single view model of my choice, not just use the built in ViewModelStore#clear:

public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.onCleared();
    }
    mMap.clear();
}

which clears all view models.


Solution

  • First, I think you should not consider doing that, because that's an implementation detail of Architecture Components library. Most possibly you should come up with a better solution as a result of adapting your use-case to match guidelines/contracts exposed by ViewModels API.

    Nevertheless, let's examine possibilities of doing that.

    Here's the code, that we should use in order to obtain a ViewModel implementation:

    val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
    

    What will this code result in, is that it will create an instance of HolderFragment, which is a retained fragment, and will attach it to this's fragment manager (might be either FragmentActivity's fragment manager or Fragment's child fragment manager).

    This HolderFragment will be added with a HolderFragment.HOLDER_TAG, thus we are able to get an instance of this fragment from the fragment manager.

    val holderFragment = supportFragmentManager.findFragmentByTag("android.arch.lifecycle.state.StateProviderHolderFragment") as HolderFragment
    

    It's the HolderFragment, that creates an instance of ViewModelStore and keeps that instance as a private field. There exists a getter for that field, but there does not exist a setter, which means, that the only way to "substitute" this object is by using reflection.

    But before doing that, let's try to write a custom implementation of ViewModelStore class:

    class MyViewModelStore : ViewModelStore() {
    
      private val mMap = HashMap<String, ViewModel>()
    
      internal fun put(key: String, viewModel: ViewModel) {
        val oldViewModel = mMap.put(key, viewModel)
        oldViewModel?.onCleared() // COMPILATION ERROR -> Cannot access 'onCleared': it is protected/*protected and package*/ in 'ViewModel'
      }
    
      internal operator fun get(key: String): ViewModel? {
        return mMap[key]
      }
    
      override fun clear() {
        for (vm in mMap.values) {
          vm.onCleared() // COMPILATION ERROR -> Cannot access 'onCleared': it is protected/*protected and package*/ in 'ViewModel'
        }
        mMap.clear()
      }
    
    }
    

    Unfortunately, we cannot do that, because ViewModel#onCleared() has a protected package access, which makes impossible for us call it outside of the android.arch.lifecycle package. Again, we can use reflection to do that (but how good is that?).

    Despite being not advised (by me), seems like that's also not achievable to do (without using reflection).