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路漫漫,共勉! 如果你有需要的话,只需私信我【进阶】即可获取