Search code examples
androiddaggerandroid-viewmodeldagger-hilt

Android Dagger Hilt: Do we need scope annotations for ViewModels?


in my app I have a MainActivity which requires access to a ViewModel. I am injecting the ViewModel using DaggerHilt and the @ViewModelInject annotation. Additionally, I have two Fragments within the Activity that require access to the same ViewModel in order to pass data to each other using observables.

The problem: I have found that whenever one of my Fragments go through onDestroy() its ViewModel is killed. This leads me to think that the Activity and Fragments are not sharing the same ViewModel.

My question: Does anyone know if we are supposed to use scope annotations for ViewModels in Dagger Hilt? I didn't see this stated in the Hilt docs or the android dev tutorials/guides. I had assumed that they were making ViewModels app level singletons, which would make sense.

If we do have to use scope annotations for ViewModels, does anyone know which level is appropriate?

This is my viewmodel code:

class MainActivityViewModel @ViewModelInject constructor(
    private val repo: Repo,
    private val rxSharedPrefsRepo: RxSharedPrefsRepo,
    private val resourcesRepo: ResourcesRepo,
    @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {

Solution

  • As per the Scoping in Android and Hilt blog post, using @ViewModelInject means that the objects you pass into the ViewModel are scoped to the ViewModel.

    The scope of the ViewModel, however, is based on how you get the ViewModel (what ViewModelStore the ViewModel is associated with) - not anything that Hilt controls. If you use by viewModels() in a Fragment, then the ViewModel is scoped to the Fragment. If you use by activityViewModels() or by navGraphViewModels(), then the ViewModel would be scoped to the activity or navigation graph, respectively.

    As mentioned in the blog post, if you want an object that is scoped to the activity and survives configuration changes, you can use Hilt's @ActivityRetainedScoped on any object and inject that object into both fragments.

    Whether you should use @ActivityRetainedScoped or a ViewModel where you control the scope separately from Hilt is covered in the blog post:

    The advantage of scoping with Hilt is that scoped types are available in the Hilt component hierarchy whereas with ViewModel, you have to manually access the scoped types from the ViewModel.

    The advantage of scoping with ViewModel is that you can have ViewModels for any LifecycleOwner objects in your application. For example, if you use the Jetpack Navigation library, you can have a ViewModel attached to your NavGraph.

    Hilt provides a limited number of scopes. You might find that you don’t have a scope for your particular use case — for example, when using nested fragments. For that case, you can fall back to scoping using ViewModel.