Kotlin 看不懂 ViewModelProviders.of(activity ?: fragment)

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

在我的 Fragment 中,我使用 ViewModelProviders 初始化了一个 ViewModel。 如果不是 null,我希望它采用它的 Activity,否则它自己 (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

似乎语言不允许我调用冲突的方法签名(在 of(Activity)of(Fragment) 之间。(这可能是可以理解的,也许编译器只需要引用一个方法而不能 link 都在同一行。) 是这样吗?

我现在必须使用

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

有更好的方法吗?

是的,这是 编译器歧义,因为您同时传递了 activitythis (fragment instance),这在 [=20= 中有不同的实现]ViewModelProviders.

另一种方法 是使用 when 条件,如下所示,(尽管您的做法也不错):

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.
        }
    }
}

除了 Jeel 的回答,我建议如果你经常需要使用这个模式,你应该在 Fragment 上定义一个扩展函数,以避免重复。例如:

fun Fragment.getViewModelProvider() =
    activity?.let(ViewModelProviders::of) ?: ViewModelProviders.of(this)

inline fun <reified T : ViewModel> Fragment.getViewModel() = 
    getViewModelProvider().get(T::class.java)

从那里,在任何 Fragment 中,您可以调用:

val viewModel: MainViewModel = getViewModel()
val viewModel = getViewModel<MainViewModel>()

它将避免获取提供程序的样板文件以及具体指定 Java class。

如果您使用的是 androidx,那么应该为生命周期添加两行:

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

并像这样使用它:

 val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)