Koin: Injecting viewmodel in fragment is attached to activity by default

Created on 19 Jul 2019  路  4Comments  路  Source: InsertKoinIO/koin

Describe the bug
Injecting a viewmodel with

startKoin {
    androidContext(this@Application)
    modules(module {
        viewModel { IntroViewModel() }
    })
}

And then getting it in a Fragment with:

private val vm: IntroViewModel by viewModel()

Creates the viewmodel attached to the parent activity, I would expect the viewmodel's lifecycle owner to be this fragment as per the default behaviour.

This looks intentional but also misaligned with how viewmodels are meant to work by design with the ViewModelProviders. To get a viewmodel in a fragment that's attached to the activity, you need to explicitly call it on the activity:

model = ViewModelProviders.of(activity!!)[SharedViewModel::class.java]

If this indeed intention, it would be good to have an option to define if I want to have a shared viewmodel (attached to activity), or individual viewmodels for each fragment.

To Reproduce
Steps to reproduce the behavior:

  1. Inject a viewmodel with viewModel {} in the Application class
  2. Create 2 fragments attached to the same activity
  3. request the viewmodel in both those fragments with by viewModel()

Expected behavior
2 Instances of the ViewModel as the default behavior and a possibility to change it to use the same instance.

Koin project used and used version (please complete the following information):
implementation "org.koin:koin-android:2.0.1"
implementation "org.koin:koin-android-viewmodel:2.0.1"

android check issue

Most helpful comment

Hello @curliq ,

I just verified the koin-android-viewmodel 2.1.0, the by viewModel() will create a new instance for each Fragment by default.

To share a VM instance, the by shareViewModel() will share activities and its fragments instance

If you check the current code, it handle the ViewModelStore like that:

fun <T : ViewModel> LifecycleOwner.getViewModelStore(
        parameters: ViewModelParameters<T>
): ViewModelStore =
        when {
            parameters.from != null -> parameters.from.invoke().viewModelStore
            this is FragmentActivity -> ViewModelStores.of(this)
            this is Fragment -> ViewModelStores.of(this)
            else -> error("Can't getByClass ViewModel '${parameters.clazz}' on $this - Is not a FragmentActivity nor a Fragment neither a valid ViewModelStoreOwner")
        }

Do you have a quick example that would reproduce that? 馃
Couldnt reproduce on the android-samples app .

All 4 comments

Hello @curliq ,

I just verified the koin-android-viewmodel 2.1.0, the by viewModel() will create a new instance for each Fragment by default.

To share a VM instance, the by shareViewModel() will share activities and its fragments instance

If you check the current code, it handle the ViewModelStore like that:

fun <T : ViewModel> LifecycleOwner.getViewModelStore(
        parameters: ViewModelParameters<T>
): ViewModelStore =
        when {
            parameters.from != null -> parameters.from.invoke().viewModelStore
            this is FragmentActivity -> ViewModelStores.of(this)
            this is Fragment -> ViewModelStores.of(this)
            else -> error("Can't getByClass ViewModel '${parameters.clazz}' on $this - Is not a FragmentActivity nor a Fragment neither a valid ViewModelStoreOwner")
        }

Do you have a quick example that would reproduce that? 馃
Couldnt reproduce on the android-samples app .

reopen it if you have a clear way to reproduce, thanks 馃憤

I'm also unable to reproduce again.

great 馃憤

Was this page helpful?
0 / 5 - 0 ratings