范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

JetpackMVVM七宗罪之一还在使用Fragment作为LifecycleOwner?

  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()      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
  解决方法
  明白了问题原因,解决思路也就清楚了: 必须要保证订阅的时机和所使用的LifecycleOwner相匹配,即要么调整订阅时机,要么修改LifecycleOwner
  在 onCreate 中订阅
  思路一是修改订阅时机,将订阅提前到  onCreate , 可以保证与 onDestory  的成对出现,但不幸的是这会带来另一个问题。
  当 Fragment 出入栈造成 View 重建时,我们需要重建后的 View 也能显示最新状态。但是由于 onCreate 中的订阅的 Observer 已经获取过 LiveData 的最新的 Value,如果 Value 没有新的变化是无法再次通知 Obsever 的
  在 LiveData 源码中体现在通知  Obsever  之前对 mLastVersion  的判断: //LiveData.java      private void considerNotify(ObserverWrapper observer) {         if (!observer.mActive) {             return;         }                   if (!observer.shouldBeActive()) {             observer.activeStateChanged(false);             return;         }         if (observer.mLastVersion >= mVersion) {// Value已经处于最新的version             return;         }                  observer.mLastVersion = mVersion;         //noinspection unchecked         observer.mObserver.onChanged((T) mData);     }
  正是为了保证重建后的 View 也能刷新最新的数据, 我们才在  onViewCreated  中完成订阅。因此只能考虑另一个思路,替换 LifecycleOwner
  使用 ViewLifecycleOwner
  Support-28 或 AndroidX-1.0.0 起,Fragment 新增了  getViewLifecycleOwner  方法。顾名思义,它返回一个与 Fragment#mView  相匹配的 LifecycleOwner,可以在 onDestroyView  的时候走到 DESTROYED  ,删除 onCreateView  中注册的 Observer, 保证了 add/remove 的成对出现。
  看一下源码,原理非常简单  //Fragment.java void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,             @Nullable Bundle savedInstanceState) {         //...                  mViewLifecycleOwner = new LifecycleOwner() {             @Override             public Lifecycle getLifecycle() {                 if (mViewLifecycleRegistry == null) {                     mViewLifecycleRegistry = new LifecycleRegistry(mViewLifecycleOwner);                 }                 return mViewLifecycleRegistry;             }         };         mViewLifecycleRegistry = null;         mView = onCreateView(inflater, container, savedInstanceState);         if (mView != null) {             // Initialize the LifecycleRegistry if needed             mViewLifecycleOwner.getLifecycle();            // Then inform any Observers of the new LifecycleOwner             mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner); //mViewLifecycleOwnerLiveData在后文介绍         } else {             //...         }     }
  基于  mViewLifecycleRegistry  创建 mViewLifecycleOwner ,      @CallSuper     public void onViewStateRestored(@Nullable Bundle savedInstanceState) {// called when onCreateView         if (mView != null) {             mViewLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);         }     }                @CallSuper     public void onDestroyView() {         if (mView != null) {             mViewLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);         }     }
  然后在  onCreateView  和 onDestroyView  时,推进到合适的生命周期。
  getViewLifecycleOwnerLiveData
  顺道提一下,与  getViewLifecycleOwner  同时新增的还有 getViewLifecycleOwnerLiveData 。从前面贴的源码中对 mViewLifecycleOwnerLiveData 的使用,应该可以猜出它的作用:它是前文讨论的思路1的实现方案,即使在 onCreate  中订阅,由于在 onCreateView  中对 LiveData 进行了重新设置,所以重建后的 View 也可以更新数据。   // Then inform any Observers of the new LifecycleOwner   mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
  需要特别注意的是,根据 MVVM 最佳实践,我们希望由 ViewModel 而不是 Fragment 持有 LiveData,所以 不再推荐使用 getViewLifecycleOwnerLiveData  最后:StateFlow 与 lifecycleScope
  前面都是以  LiveData  为例介绍对 ViewLifecycleOwner 的使用, 如今大家也越来越多的开始使用协程的 StateFlow  , 同样要注意不要错用 LifecycleOwner
  订阅 StateFlow 需要  CoroutineScope , AndroidX 提供了基于 LifecycleOwner 的扩展方法 val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope     get() = lifecycle.coroutineScope
  当我们在 Fragment 中获取  lifecycleScope  时,切记要使用 ViewLifecycleOwner class MyFragment : Fragment() {      val viewModel: MyViewModel by viewModel()      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {         super.onViewCreated(view, savedInstanceState)          //使用 viewLifecycleOwner 的 lifecycleScope         viewLifecycleOwner.lifecycleScope.launch {             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {                 viewModel.someDataFlow.collect {                     updateUI(it)                 }             }         }     } }
  注意此处出现了一个  repeatOnLifecycle(...) , 这跟本文无关,但是将涉及到下一宗罪的剧情,敬请期待。 最后
  在这里就还分享一份由大佬亲自收录整理的Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取

马斯克牛皮吹破了!谁也没想到,打脸竟来得如此之快雷军早年在公开场合表示站在风口上,猪都能飞起来!传统汽车厂商在积极寻求转型的同时,一大波新能源汽车公司的崛起参与市场竞争,越来越多的科技公司也纷纷官宣造车,或许新能源汽车成为了下一热点NFT大热!等等,啥是NFT呀?它能复制吗?它安全吗?万物皆可NFT!2021年8月12日,蚂蚁链推出新付款码皮肤NFT收藏品青蛇劫起小白,该付款码皮肤NFT创作者是天津端盒拿趣科技有限公司,存证时间为2021年8月12日100131蔚来饭圈化了?到底谁能代表蔚来车主你成功让蔚来车主变成了笑话!一场致命车祸将蔚来车主群体彻底撕裂。7月30日,上善若水投资管理创始人林文钦驾驶蔚来启用自动驾驶功能后,发生交通事故不幸逝世。目前事故原因还在调查之中。互联网需要一个秦始皇这几天我一直没睡好,因为我的枕头被阿半坐了一下。这个枕头原本是一个用着特别舒服的记忆棉枕头,但自从被阿半坐过之后,它就失忆了。它忘记了自己曾经是一个圆润饱满的大枕头,永远地变成了大有效保障互联网处方用药安全(人民时评)来源人民网确保用药安全,事关人民群众身体健康,也是推动互联网医疗健康新业态健康有序发展的重要方面如今,随着互联网医院互联网医疗平台增多,同时网上复诊也能进行医保报销,越来越多的人尝3D人脸识别!首次揭秘屏下摄像量产创新引领四大发明点2020年,中兴通讯发布了全球首个商用屏下摄像手机中兴Axon20,拉开了屏下摄像手机序幕,2021年中兴通讯再次率先领跑发布了Axon30并引发热议。随着屏下摄像领域陆续有厂商推有效保障互联网处方用药安全来源人民网人民日报如今,随着互联网医院互联网医疗平台增多,同时网上复诊也能进行医保报销,越来越多的人尝试在网上复诊购药。尤其是对很多慢性病患者来说,在互联网医院进行复诊购药配送医保最惨阵营?腾讯阿里美团快手,市值蒸发8万亿!真被抛弃?腾讯火速护盘,段永平二次助阵在反垄断风暴,以及减税优惠或被取消的传闻打压下,互联网巨头们的股价,正在经历寒冬的洗礼。8月19日,港股的投资者,又被毒打了5个多小时。当天,香港股市开盘后仅挣扎了十几分钟,就掉头我见物联多妩媚!物联网联盟会员风采系列之(九十六)一我见物联多妩媚!物联网联盟会员风采系列之联盟会员单位研华(中国)公司北京分公司研华是全球领先的网络平台(eplatform)服务供应商。自1983年创立以来,研华始终致力于为工业数字和模拟芯片区别(一)芯片根据电平敏感度可区分为数字芯片和模拟芯片前者只需要区分是逻辑高电平还是低电平,而后者需要精确知道电平具体的数值。比如,工作电平是3V,那么如果外界输入0到0。3V都被认定为低电旗舰芯片凉了?三星新品放弃自家处理器全系骁龙898如今手机处理器的阵营已经逐渐地开始明朗起来,苹果的A系依然一骑绝尘,安卓阵营中处于霸主地位的高通,由于失去海思麒麟这一强劲的对手,使得其市场占有率再一次增长,虽然还有三星联发科紫光
昨夜豪言犹在耳,今朝销量成笑谈,吉利是豪言还是空谈?4年前,2017年全球汽车论坛在重庆举行,时任吉利汽车集团总裁兼首席执行官安聪慧表示如何顺势而为如何提升企业竞争力?是所有中国汽车企业在思考的问题。吉利将继续推动企业快速的可持续的无线很稳!雷柏V500PRO多模版机械键盘百元性价比,五台设备切换闲舒隐士,守城皇族,无畏战士,天生领袖你是哪一个?强大的基因总显示着强烈的家族印记标志性的五官,一看便知那是谁家那小谁吧!?超高辨识度的DNA至黑的夜,光明不灭V500PRO机械键机身最薄5mm,可以切换4台设备,雷柏E9350G多模无线刀锋键盘发布忙碌的工作,奔波的生活,万物肆意生长,尘埃与曙光升腾,不过多惋惜遗憾,厚积之后的薄发,拥抱更高等级的自由。我们启动了一个项目自由无负,在发展产品的过程中,美观之余更注重温和实用。本大尺寸高端智能纯电SUV,一汽大众ID。6CROZZ正式开启交付7月18日,主题为科技为你的一汽大众ID。Day科技嘉年华暨ID。6CROZZ交付之夜在成都隆重举办来自全国超过500位一汽大众ID。粉丝和数百名媒体记者,超千人参与嘉年华体验并见智能大空间,大众e起行上汽大众ID。6X高光派对圆满落幕7月11日,迎着避暑胜地的习习晚风,上汽大众ID。6X高光派对在信阳鸡公山顶闪亮开启。活动现场,华中核心媒体嘉宾悉数到场,通过ID初体验ID再判断ID终决选等环节体验,深刻感受ID极致呵护宝宝穿衣健康!TCL复式分区洗护黑科技成未来行业主流后疫情时代之下,健康成为了用户的绝对刚需,其产品使用需求从曾经的解决温饱过得更好更有艺术仪式感升级转变为如今的更智能更健康,此外,国家相关政策在用户健康方面也起到了一定的促进作用,媲美买楼的快落!上苏宁以旧换新华为Mate40新机就懂了近日,抖音都被一条壕视频刷屏了!有网友爆料张庭夫妇花费17亿人民币买下黄浦江边整栋楼。这座楼的位于上海核心地段,黄浦江畔。前几日有网友在社交平台晒出了一段张庭与演员陶虹同框的视频。还在为手机存储空间不足发愁?试试这款设备,简单一步搞定还记得十年前的手机拍照像素吗?这些年手机不仅摄像头从一个变成了多个,像素也是突飞猛进,从当年了几十万像素升级到了如今1亿像素。拍照是为了记录和留念,那么高清细腻的画面自然也就成了大同级最强PLUS!传祺GS4PLUS预售13。5万元起6月12日,传祺GS4PLUS在郑州2021第十届中原国际车展亮相预售发布。作为销量最快破百万的中国品牌SUV,传祺GS4家族已经拥有近130万基盘车主,此次亮相预售发布的家族新成骁云动力焕芯升级第二代宋Pro正式亮相郑州怡人初夏,清风沁脾。6月12日,骁云动力焕芯升级第二代宋Pro芯动惠民狂享季郑州站在比亚迪汽车河南文华店如期启幕。活动当天,郑州当地的媒体大咖齐聚于此,共同见证第二代宋Pro的芯动实力带货拉动汽车消费2021第十届中原国际车展圆满落幕6月14日,2021第十届中原国际汽车展览会暨中原汽车文化旅游节在郑州国际会展中心圆满落幕!4天展期累计吸引20。5万人次观展,累计销售车辆超过6500台,销售金额突破10亿元。截