【问题标题】:Livedata has different values in different activitiesLivedata在不同的活动中具有不同的价值
【发布时间】:2020-08-24 15:33:57
【问题描述】:

我在我的 kotlin 应用程序中使用 Android MVVM。我有一个UserViewModel 类,其中包含应用程序运行所需的所有用户相关数据。

class UserViewModel(application: Application) : AndroidViewModel(application) {

    val currentUid = MutableLiveData<String>()
    var currentUser = MutableLiveData<User>()
    // More code here...
}

currentUser 在启动时从 Firestore 中获取,并可供所有活动使用。我想访问User 中的currentPlan 对象。我这样做:

someFunc(userViewModel.value!!.currentPlan!!)

这在我的HomeFragment 在我的MainActivity 中运行良好。 HomeFragment 有一个消息按钮,可将用户带到ChatActivty

问题是当我在ChatActivity 中执行相同的函数调用时,我得到了这个错误:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.**, PID: 30420
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.makeshaadi/com.makeshaadi.ChatActivity}: kotlin.KotlinNullPointerException
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2946)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3081)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:201)
        at android.app.ActivityThread.main(ActivityThread.java:6810)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
     Caused by: kotlin.KotlinNullPointerException
        at com.makeshaadi.ChatActivity.onCreate(ChatActivity.kt:52)
        at android.app.Activity.performCreate(Activity.java:7224)
        at android.app.Activity.performCreate(Activity.java:7213)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2926)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3081) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:201) 
        at android.app.ActivityThread.main(ActivityThread.java:6810) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873) 
I/Process: Sending signal. PID: 30420 SIG: 9

为什么userVM.valueChatActivity 中变为空。我做错了什么?

【问题讨论】:

  • ChatActivity 中有一个不同的 ViewModel 实例。 ViewModel 不是单例
  • 哇!我该如何解决?我这样声明它们:private val userVM: UserViewModel by viewModels()@tyczj
  • 长话短说,您希望 ViewModel 不是单例,它们是为每个活动创建的。您可以创建某种类似于单例的“UserService”类,然后将此类传递给您的视图模型。您可能想考虑使用Dependency Injection
  • 在单个 Activity 中使用多个 Fragment 而不是多个 Activity。在Fragment中使用by activityViewModels(),不同的Fragment会得到同一个实例。

标签: android kotlin mvvm


【解决方案1】:

一些理论

有多种方法可以创建ViewModel 类的实例(也许我错过了一些):

// #1 lazy loaded class level variable. 
// Activity serves as view model store
val vm: UserViewModel by activityViewModels()

// #2 lazy loaded class level variable. 
// Activity or Fragment serves as a view model store owner.
// Depends on from which class it is used.
val vm: UserViewModel by viewModels()

// #3 A variable defined in a function scope. 
// `this` is either Activity or a Fragment. The same as #2 but not lazy.
val vm = ViewModelProvider(this).get(UserViewModel::class.java)

// #4 The same as the third option but with factory parameter. 
// Used in cases when view model constructor accepts custom arguments.
// Factory takes care of passing correct parameters.
val vm = ViewModelProvider(this, factory).get(UserViewModel::class.java)

常见的是你总是隐式或显式地使用view model store owner。它要么是一个活动,要么是一个片段。只要视图模型存储存在,视图模型就会存在。它受 Activity/Fragment 生命周期的约束。当 Fragment 被销毁时,该片段创建的所有视图模型也将被销毁(如果有循环引用)。 Activity 也是如此。

这也是shared view models 的创建方式。 Fragments 可以重复使用相同的视图模型只要它由 Activity 拥有。这就是为什么我们有两个延迟加载函数:by activityViewModels()by viewModels()。第一个主要用于共享视图模型。

如何解决这个问题?

要么不使用视图模型并创建单例object,要么使用单例视图模型(强烈不推荐)。

如果您的应用程序在其生命周期内共享一些数据,并且您确定应该从一个地方存储和访问这些数据 - 单例会有所帮助。

这就像连接到数据库。通常,只有一个打开的连接,或者它是根据请求打开和关闭的,但我没有看到后一种数据库实现,通常是文件。

我建议使用 Kotlin 的objects:

object MySingleton {
    val currentUid = MutableLiveData<String>()
    var currentUser = MutableLiveData<User>()
    // More code here...
}

【讨论】:

    【解决方案2】:

    更新: 创建类UserRepository 并注入单例UserRepository(手动,或Dagger,Koin,..)

    【讨论】:

    • 在不同的活动中使用时无济于事。
    • 更新答案。
    • 这确实是一种选择!
    猜你喜欢
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    • 2021-08-17
    • 1970-01-01
    • 2021-12-05
    • 2021-03-07
    • 1970-01-01
    • 2016-02-01
    相关资源
    最近更新 更多