Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple instances of android ViewModel #539

Closed
warcello opened this issue Jul 30, 2019 · 4 comments
Closed

Multiple instances of android ViewModel #539

warcello opened this issue Jul 30, 2019 · 4 comments
Labels
question Usage question

Comments

@warcello
Copy link

I've noticed a bug that is very similar to the #145

If ViewModel is created as a constructor parameter of another ViewModel then it can be created multiple times.

To Reproduce

  1. Create a module with two ViewModels (for example FirstViewModel and SecondViewModel ) where SecondViewModel has FirstViewModel as a constructor parameter.
    Example:
val koinModule: Module = module {
    viewModel { FirstViewModel() }
    viewModel { SecondViewModel(*get()*) } //<---- this parameter is a FirstViewModel instance (maybe here should be sth like `getViewModel()` instead of `get()` ?)
}
  1. Create some class and inject both viewModels
class SomeClass {
    val firstVM: FirstViewModel by viewModel()
    val secondVM: SecondViewModel by viewModel()
}
  1. In effect SomeClass has different instance of FirstViewModel than SecondViewModel

Expected behavior
Only one instance of FirstViewModel should be created.

Koin project used and used version (please complete the following information):
koin-core version 2.0.1
koin-androidx-viewmodel version 2.0.1

@cetonek
Copy link

cetonek commented Aug 21, 2019

My theory is that this is expected behavior, because for viewModel to be injected and hooked to correct ViewModelStoreOwner it needs to be injected from that context -> ie by viewModel {} or by sharedViewModel {} in activity or fragment. This is how koin resolves a viewModel:

/**
 * resolve instance
 * @param parameters
 */
fun <T : ViewModel> Koin.getViewModel(parameters: ViewModelParameters<T>): T {
    val vmStore: ViewModelStore = parameters.owner.getViewModelStore(parameters)
    val viewModelProvider = rootScope.createViewModelProvider(vmStore, parameters)
    return viewModelProvider.getInstance(parameters)
}

Notice parameters:

class ViewModelParameters<T : Any>(
    val clazz: KClass<T>,
    val owner: LifecycleOwner,
    val qualifier: Qualifier? = null,
    val from: ViewModelStoreOwnerDefinition? = null,
    val parameters: ParametersDefinition? = null
)

typealias ViewModelStoreOwnerDefinition = () -> ViewModelStoreOwner

This way koin is provided LifecycleOwner and ViewModelStoreOwner (optionally) from which it will correctly resolve wheter to create new viewModel or return existing instance for given activity/fragment. I think for your constructor injection Koin just handles it like a regular object and simply creates new instance.

But your use case seems weird to me, If I want to communicate between two viewModels I do so through some repository or some kind of shared data holder that has scope "longer" than given viewModels - I avoid communicating direcly between them

@rexih
Copy link

rexih commented Aug 28, 2019

as explained by @Ejstn

i think maybe you can try:

val koinModule: Module = module {
    viewModel { FirstViewModel() }
    viewModel {  (model: FirstViewModel) -> SecondViewModel(model) }

class SomeClass {
    val firstVM: FirstViewModel by viewModel()
    val secondVM: SecondViewModel by viewModel{ parametersOf(firstVM)}
}

@mcungl
Copy link

mcungl commented Sep 23, 2019

viewModel { FirstViewModel() } is creating a factory bean definition, which means you always get a new instance.

Maybe you could use the single bean definition combined with scopes tying it to the activity.

@arnaudgiuliani
Copy link
Member

ViewModel are more intended to be tied to the Activity/Fragment lifecycle stuff ... else extract component from them. Don't try to inject VM between them, not sure this is a good use case 🤔

@arnaudgiuliani arnaudgiuliani added the question Usage question label Oct 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Usage question
Projects
None yet
Development

No branches or pull requests

5 participants