纠纷奇闻社交美文家庭
投稿投诉
家庭城市
爱好生活
创业男女
能力餐饮
美文职业
心理周易
母婴奇趣
两性技能
社交传统
新闻范文
工作个人
思考社会
作文职场
家居中考
兴趣安全
解密魅力
奇闻笑话
写作笔记
阅读企业
饮食时事
纠纷案例
初中历史
说说童话
乐趣治疗

JetpackMVVM七宗罪之三在onViewCreated

6月6日 顾昀汐投稿
  ViewModel数据的首次加载时机?
  在MVVM中,ViewModel的重要职责是解耦View与Model。View向ViewModel发出指令,请求数据View通过DataBinding或LiveData等订阅ViewModel的数据变化
  关于订阅ViewModel的时机,大家一般放在onViewCreated,这是没有问题的。但是一个常犯的错误是将ViewModel中首次的数据加载也放到onViewCreated中进行:DetailTaskViewModel。ktclassDetailTaskViewModel:ViewModel(){privatevaltaskMutableLiveDataTask()valtask:LiveDataTasktaskfunfetchTaskData(taskId:Int){viewModelScope。launch{task。valuewithContext(Dispatchers。IO){TaskRepository。getTask(taskId)}}}}DetailTaskFragment。ktclassDetailTaskFragment:Fragment(R。layout。fragmentdetailedtask){privatevalviewModel:DetailTaskViewModelbyviewModels()overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super。onViewCreated(view,savedInstanceState)订阅ViewModelviewMode。uiState。observe(viewLifecycleOwner){updateui}请求数据viewModel。fetchTaskData(requireArguments()。getInt(TASKID))}}
  如上,如果ViewModel在onViewCreated中请求数据,当View因为横竖屏等原因重建时会再次请求,而我们知道ViewModel的生命周期长于View,数据可以跨越View的生命周期存在,所以没有必要随着View的重建反复请求。
  正确的加载时机
  ViewModel的初次数据加载推荐放到init{}中进行,这样可以保证ViewModelScope中只加载一次TasksViewModel。ktclassTasksViewModel:ViewModel(){privatevaltasksMutableLiveDataListTask()valtasks:LiveDataListTaskuiStateinit{viewModelScope。launch{tasks。valuewithContext(Dispatchers。IO){TasksRepository。fetchTasks()}}}}LiveDataKTXBuilder
  此外lifecyclelivedataktx提供的LiveDataKTXBuilder可以在创建LiveData的同时进行数据请求,无需创建MutableLiveData,写法更简洁:
  implementationandroidx。lifecycle:lifecyclelivedataktx:latestversionvaltasks:LiveDataResultliveData{emit(Result。loading())try{emit(Result。success(repo。fetchData()))}catch(ioException:Exception){emit(Result。error(ioException))}}
  Note:此种KTXBuilder只适用于数据仅加载一次的情况,如果后续有用户动态触发的数据请求,则还需要借助MutableLiveData来实现。设置ViewModel的初始化参数
  如果在ViewModel构造函数中请求数据,当需要参数时该如何传入呢?比如我们最开头例子中需要传入一个TaskId。1。构造参数
  最容易想到的方法是通过构造参数传入。classDetailTaskViewModel(privatevaltaskId:Int):ViewModel(){。。。init{viewModelScope。launch{tasks。valueTasksRepository。fetchTask(taskId)}}}
  需要注意不能直接调用ViewModel的构造函数构造,这样无法将ViewModel存入ViewModelStore。
  此时需要定义一个ViewModelProvider。Factory:classTaskViewModelFactory(valtaskId:Int):ViewModelProvider。Factory{overridefunT:ViewModel?create(modelClass:ClassT):TmodelClass。getConstructor(Int::class。java)。newInstance(taskId)}
  然后在Fragment中,用此Factory创建ViewModelclassDetailTaskFragment:Fragment(R。layout。fragmentdetailedtask){privatevalviewModel:DetailTaskViewModelbyviewModels{TaskViewModelFactory(requireArguments()。getInt(TASKID))}overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super。onViewCreated(view,savedInstanceState)。。。}}2。使用SavedStateHandler
  Fragment1。2。0或者Activity1。1。0起,可以使用SavedStateHandle作为ViewModel的参数。SavedStateHandle可以帮助ViewModel实现数据持久化,同时可以传递Fragment的arguments给ViewModel。
  关于如何使用SavedStateHandle对数据进行持久化,由于不是本文重点不做介绍,这里只展示如何通过SavedStateHandle获取arguments
  implementationandroidx。lifecycle:lifecycleviewmodelsavestate:latestversion
  SavedStateHandle版本的ViewModel定义如下:classTaskViewModel(savedStateHandle:SavedStateHandle):ViewModel(){。。。init{viewModelScope。launch{tasks。valueTasksRepository。fetchTask(savedStateHandle。getInt(TASKID))}}}
  Fragment中创建ViewModel如下:classDetailTaskFragment:Fragment(R。layout。fragmentdetailedtask){privatevalviewModel:TaskViewModelbyviewModels{SavedStateViewModelFactory(requireActivity()。application,requireActivity(),arguments将arguments作为默认参数传递给SavedStateHandler)}overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super。onViewCreated(view,savedInstanceState)。。。}}
  其中,SavedStateViewModelFactory是关键,它会在构造ViewModel的时候,传入SavedStateHandler3。自定义扩展方法
  前两种方法的模板代码较多,这里推荐一个自定义的扩展方法viewModelByFactory,可以进一步简化代码typealiasCreateViewModel(handle:SavedStateHandle)ViewModelinlinefunreifiedVM:ViewModelFragment。viewModelByFactory(defaultArgs:Bundle?null,noinlinecreate:CreateViewModel{valconstructorfindMatchingConstructor(VM::class。java,arrayOf(SavedStateHandle::class。java))constructor!!。newInstance(it)}):LazyVM{returnviewModels{createViewModelFactoryFactory(this,defaultArgs,create)}}inlinefunreifiedVM:ViewModelFragment。activityViewModelByFactory(defaultArgs:Bundle?null,noinlinecreate:CreateViewModel):LazyVM{returnactivityViewModels{createViewModelFactoryFactory(this,defaultArgs,create)}}funcreateViewModelFactoryFactory(owner:SavedStateRegistryOwner,defaultArgs:Bundle?,create:CreateViewModel):ViewModelProvider。Factory{returnobject:AbstractSavedStateViewModelFactory(owner,defaultArgs){overridefunT:ViewModel?create(key:String,modelClass:ClassT,handle:SavedStateHandle):T{Suppress(UNCHECKEDCAST)returncreate(handle)as?T?:throwIllegalArgumentException(Unknownviewmodelclass!)}}}PublishedApiinternalfunTfindMatchingConstructor(modelClass:ClassT,signature:ArrayClass):ConstructorT?{for(constructorinmodelClass。constructors){valparameterTypesconstructor。parameterTypesif(Arrays。equals(signature,parameterTypes)){returnconstructorasConstructorT}}returnnull}
  使用时的效果如下:classDetailTaskFragment:Fragment(R。layout。fragmentdetailedtask){privatevalviewModelbyviewModelByFactory(arguments)overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super。onViewCreated(view,savedInstanceState)。。。}}
  除了SavedStateHandler以外如果还希望增加更多参数,还可以自定义CreateViewModel4。依赖注入
  最后看一下如何使用依赖注入传参。以Hilt为例,Hilt天然支持ViewModel的依赖注入,本质上也是基于SavedStateHandler实现的HiltViewModelclassDetailedTaskViewModelInjectconstructor(privatevalsavedStateHandle:SavedStateHandle):ViewModel(){。。。}
  添加HiltViewModel注解,并使用Inject注解构造函数。除了SavedStateHandle以外,也可以注入其他更多参数
  ViewModel的使用处,别忘添加AndroidEntryPointAndroidEntryPointclassDetailedTaskFragment:Fragment(R。layout。fragmentdetailedtask){privatevalviewModel:DetailedTaskViewModelbyviewModels()overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super。onViewCreated(view,savedInstanceState)。。。}}
  前三种方式或多或少都要使用ViewModelProvider。Factory来构造ViewModel,而Hilt避免了Factory的使用,在写法上最为简单。最后
  在这里就还分享一份由大佬亲自收录整理的Android学习PDF架构视频面试文档源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取
投诉 评论 转载

时隔六年再度重启,WCG2019总奖池公布!单项奖金最高8万感谢IT之家网友重案组黄启法的线索投递!IT之家4月4日消息暂停六年的电竞奥运会WCG终于回归,新一届WCG2019的比赛将于2019年7月18日21日在西安举办。赛方此……JetpackMVVM七宗罪之三在onViewCreatedViewModel数据的首次加载时机?在MVVM中,ViewModel的重要职责是解耦View与Model。View向ViewModel发出指令,请求数据View通过Da……最美客服小姐姐!被用户斥责6小时,痛哭后一秒变脸微笑面对近日,某互联网公司客服被用户斥责6个小时,挂掉电话瞬间崩溃的视频在网上热传。接入下一个电话前,客服小姐姐努力平复情绪,瞬间变脸坚持微笑工作,让众多网友觉得既心疼又励志。在……腾讯吃鸡游戏无限法则一周年,Steam免费领取DLCIT之家9月21日消息据Steam官方消息,腾讯吃鸡游戏《无限法则(RingofElysium)》一周年,现在可以在Steam游戏商城免费领取DLC(需要先有本体,本体为免费游……刚刚,央行连夜宣布,封杀全球所有非主权国家信用背书的虚拟币一hr这一天,果然来临。一周前,国家正式下发通知,将中国自己的法定数字人民币接入支付宝。再加上中国的最大6个巨无霸银行。工商、农业、中、建、交、邮储六大国有银行!同……299元,国行Switch马力欧奥德赛马力欧卡丁车8实体卡带感谢IT之家网友立立立立立的线索投递!IT之家4月15日消息腾讯NintendoSwitch官方今日宣布,《超级马力欧奥德赛》与《马力欧卡车8豪华版》实体卡带现已正式开售……腾讯王者荣耀导演争霸赛,神仙操作200杀孙尚香被封号IT之家8月23日消息据腾讯游戏安全中心发布,针对近期的一局200杀神仙操作,王者荣耀孙尚香喜提封神榜,相关演员一律进行全服封号半年的处罚。在近一周在王者荣耀排位赛中,有……英雄联盟自走棋云顶之弈更新海克斯科技上线IT之家8月20日消息据英雄联盟官方介绍,8月20日凌晨,英雄联盟官方发布9。16版本云顶之弈更新公告,海克斯科技上线。图自《英雄联盟》官方据了解,在云顶之弈9。1……2019日本文具又玩出了哪些新花样?日本ISOT文具大赏:ISOT(InternationalStationaryOfficeProductsFair)亚洲最大的文具办公用品展览会,也是世界五大文具展会之一……官方详解王者荣耀不良语音举报功能IT之家9月27日消息游戏中发送不良信息的情况时有发生。在《王者荣耀》中,发送不良文字会被系统识别后显示号,且不良发言者被举报后还会扣除信誉分。但部分玩家却通过语音的形式辱骂他……中国古代四大美女齐聚,腾讯王者荣耀新英雄西施正式上线IT之家9月24日消息作为稷下学院魔道少女的西施,曾是无主之地有名的探宝者。但她童年首次倒卖捡来的宝贝,就意外欠下了一大笔债务。在躲债时,她闯进了女神的古祠堂,并带出来一条富有……王者荣耀新英雄西施技能公布沉鱼少女峡谷控制大师IT之家9月18日消息作为稷下学院魔道少女的西施,曾是无主之地有名的探宝者。但她童年首次倒卖捡来的宝贝,就意外欠下了一大笔债务。在躲债时,她闯进了女神的古祠堂,并带出来一条富有……
王者荣耀KPL秋季总决赛开赛,汪苏泷胡彦斌陈粒将相继亮相英雄联盟真实伤害乐团皮肤载入框公布快乐风男变DJ亚索魔兽争霸3重制版多人模式测试即将开启,目前可申请测试资格继KDA之后,英雄联盟虚拟嘻哈乐团真实伤害正式诞生任天堂谈与腾讯合作原因有最大规模游戏市场OPPO为英雄联盟LPL赛区FPX战队加油助威,点亮上海环球王者荣耀钻石夺宝皮肤哪吒次元突破即将上线脚踩悬浮无人机,手持官方爆料王者荣耀王者模拟战正式版棋盘格数量增至82158加密行业可再生能源采矿现状手机系统到底该不该升级?IGN为Steam背书抽成30是行业标准,12属特立独行英雄联盟S9全球总决赛入围赛淘汰赛结束,小组赛最终分组出炉

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找美丽时装彩妆资讯历史明星乐活安卓数码常识驾车健康苹果问答网络发型电视车载室内电影游戏科学音乐整形