Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin does not understand ViewModelProviders.of(activity ?: fragment)

Inside my Fragment I initialize a ViewModel using ViewModelProviders. I want it to take its Activity if not null, otherwise itself (Fragment).

private val viewModel: MainViewModel by lazy {
    ViewModelProviders.of(activity ?: this).get(MainViewModel::class.java)
}

None of the following functions can be called with the arguments supplied.

  • of(Fragment) defined in androidx.lifecycle.ViewModelProviders
  • of(FragmentActivity) defined in androidx.lifecycle.ViewModelPro

It seems the language does not allow me to invoke conflicting method signatures (between of(Activity) and of(Fragment). (It might be understandable, maybe the compiler has to reference only one method and cannot link to both on the same line.) Is that so?

I now have to use

    activity?.let {
        ViewModelProviders.of(it).get(MainViewModel::class.java)
    } ?: run {
        ViewModelProviders.of(this).get(MainViewModel::class.java)
    }

Is there any better way of doing this?

like image 593
shkschneider Avatar asked Oct 15 '18 07:10

shkschneider


People also ask

What can I use instead of ViewModelProviders?

As source code mentioned, the static method ViewModelProviders#of was deprecated. This solution is using 'by viewModels()' instead.

Is ViewModelProviders deprecated?

Stay organized with collections Save and categorize content based on your preferences. This class is deprecated. Use the constructors for ViewModelProvider directly.

What is SavedStateHandle?

The SavedStateHandle class is a key-value map that allows you to write and retrieve data to and from the saved state through the set() and get() methods.


2 Answers

Yes, it's compiler ambiguity, because you're passing activity & this (fragment instance) at the same time which has different implementations in ViewModelProviders.

Another approach to do this is by using when condition like below, (although your approach is also good):

private val viewModel: MainViewModel by lazy {
    return@lazy when {
        activity != null -> {
            ViewModelProviders.of(activity as FragmentActivity).get(MainViewModel::class.java) // you can either pass activity object
        }
        else -> {
            ViewModelProviders.of(this).get(MainViewModel::class.java) // or pass fragment object, both are not possible at same time.
        }
    }
}
like image 172
Jeel Vankhede Avatar answered Nov 02 '22 09:11

Jeel Vankhede


if you are using androidx so should add two lines for lifecycle :

 def lifecycle_version = "2.2.0-rc02"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"

and use it like this :

 val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
like image 36
Sana Ebadi Avatar answered Nov 02 '22 07:11

Sana Ebadi