Jetpack MVVM七宗罪之一:拿Fragment当LifecycleOwner

本文探讨了在Jetpack MVVM中将Fragment作为LifecycleOwner的问题,指出这可能导致重复订阅和内存泄漏。作者分析了原因并提供了解决方案,包括在onCreate中订阅、使用ViewLifecycleOwner和getViewLifecycleOwnerLiveData。同时,强调了StateFlow和lifecycleScope的正确使用,以及避免使用Fragment持有LiveData的重要性。
摘要由CSDN通过智能技术生成

首先承认这个系列有点标题党,Jetpack 的 MVVM 本身没有错,错在开发者的某些使用不当。本系列将分享那些 AAC 中常见的错误用法,指导大家打造更健康的应用架构

Fragment 作为 LifecycleOwner 的问题

MVVM 的核心是数据驱动UI,在 Jetpack 中,这一思想体现在以下场景:Fragment 通过订阅 ViewModel 中的 LiveData 以驱动自身 UI 的更新

关于订阅的时机,一般会选择放到 onViewCreated 中进行,如下:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.liveData.observe(this) { // Warning : Use fragment as the LifecycleOwner
           updateUI(it) 
        } 

}
复制代码

我们知道订阅 LiveData 时需要传入 LifecycleOwner 以防止泄露,此时一个容易犯的错误是使用 Fragment 作为这个 LifecycleOwner,某些场景下会造成重复订阅的Bug。

做个实验如下:

val handler = Handler(Looper.getMainLooper())

class MyFragment1 : Fragment() {
    val data = MutableLiveData<Int>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        tv.setOnClickListener {
            parentFragmentManager.beginTransaction()
                .replace(R.id.container, MyFragment2())
                .addToBackStack(null)
                .commit()

            handler.post{ data.value = 1 }
        }

        data.observe(this, Observer {
            Log.e("fragment", "count: ${data.value}")
        })

}
复制代码

当跳转到 MyFragment2 然后再返回 MyFragment1 中时,会打出输出两条log

E/fragment: count: 1
E/fragment: count: 1
复制代码

原因分析

LiveData 之所以能够防止泄露,是当 LifecycleOwner 生命周期走到 DESTROYED 的时候会 remove 调其关联的 Observer

//LiveData.java

@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
   if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
          removeObserver(mObserver);
          return;
   }
   activeStateChanged(shouldBeActive());

}
复制代码

前面例子中,基于 FragmentManager#replace 的页面跳转,使得 MyFragment1 发生了从 BackStack 的出栈/入栈,由于 Framgent 实例被复用并没有发生 onDestroy, 但是 Fragment的 View 的重建导致重新 onCreateView, 这使得 Observer 被 add 了两次,但是没有对应的 remove。

所以归其原因, 是由于 Fragment 的 Lifecycle 与 Fragment#mView 的 Lifecycle 不一致导致我们订阅 LiveData 的时机和所使用的 LivecycleOwner 不匹配,所以在任何基于 replace 进行页面切换的场景中,例如 ViewPager、Navigation 等会发生上述bug

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值