首页 > 解决方案 > ViewModel 没有分离观察者并导致空指针异常

问题描述

这是我遇到的一个奇怪的错误,我开始认为我的片段的观察者在我弹出那个片段后仍在观察。

所以,我有两个使用相同视图模型的片段,但它们不与活动共享,视图模型实例适用于每个片段

片段B()

   private val viewModel by viewModels<OrderViewModel> { VMOrderFactory(
            OrderRepoImpl(
                OrderDataSource()
            )
        ) }
  ...

           viewModel.fetchOrderStatus(trackingDetails.orderId).observe(viewLifecycleOwner, Observer { result -> ... }

现在,我使用 from FragmentA() Flow 协程在我看来实时保持更新。

订单视图模型

 fun fetchOrderStatus(orderId: String) = liveData(Dispatchers.IO){
        emit(Resource.Loading())
        try{
            repo.getOrderStatus(orderId).collect { status ->
                emit(status)
            }
        }catch (e:Exception){
            emit(Resource.Failure(e))
        }
    }

现在是这样的情况

当我在 FragmentA() 中时,它可以工作并获取订单。

现在,如果我FragmentB()再次返回 FragmentA() 并尝试FragmentA()使用此视图模型的另一个实例和另一个方法从中删除,则 fetchOrderStatus fromFragmentB()被执行,并且由于我弹出此片段,它返回一个 nullPointerException

因此,奇怪的是,使用 viewLifeCycleOwner 时,这个观察者在返回 FragmentA() 时并没有与 FragmentB() 分离,而另一个奇怪的事情是,当我迅速从 FragmentB() 回到 FragmentA() 并尝试删除一个订购,但如果我稍等片刻(2 或 3 秒,直到流程附加并请求数据),这将正常工作

可能会发生什么以及我的观察员会发生什么?

片段A()

Fragment A 不使用 FragmentB() 中的 fetchOrderStatus 方法

 private val viewModel by viewModels<OrderViewModel> { VMOrderFactory(
            OrderRepoImpl(
                OrderDataSource()
            )
        ) }

 viewModel.deleteOrder(adapter.getItem(position).orderId).observe(viewLifecycleOwner, Observer { result -> ... }

错误在这里抛出,但不是关于 deleteOrder 方法,而是来自 FragmentB() fetchOrderStatus 方法,这很奇怪,因为此时 FragmentB() 被弹出并且它的方法不应该获取任何数据。

也许我需要在弹出这个 fragmentB 时终止流程,但是当它获取并交付时我在 datasource 方法中执行它

  awaitClose { subscription.remove() }

标签: androidandroid-fragmentskotlinviewmodelandroid-architecture-components

解决方案


如果您创建 ViewModel 并提供 Fragment 上下文,则 ViewModel 会在 Fragment 被销毁时被销毁。如果您希望 ViewModel 比片段更长寿,那么您需要使用活动上下文启动它activityViewModel()


推荐阅读