作者:碎星
简介
ViewModel在架构中用于承载业务逻辑和作为容器保存屏幕状态,它可以缓存界面的状态,并且能在配置变更后持久保留相应的界面状态。
在jetpack套件中,ViewModel随lifecycle一起提供。
优势
简介
ViewModel在架构中用于承载业务逻辑和作为容器保存屏幕状态,它可以缓存界面的状态,并且能在配置变更后持久保留相应的界面状态。
在jetpack套件中,ViewModel随lifecycle一起提供。
优势
- 可以持久的保持界面状态:一是界面因配置变更导致的重建,不会销毁内存中的;二是可以借助SavedStateHandle在进程销毁-重建过程中恢复数据。
- ViewModel具有作用域(如:Activity、Fragment等),ViewModel中的异步工作将被限定在这个Lifecycle上执行。
- ViewModel可用用来承载之前处于界面层的部分业务逻辑:将数据层传递的数据处理成界面状态。
- 可以作为桥梁在Activity与Fragment、Fragment与Fragment之间共享数据。
使用
定义
// 直接继承ViewModel
class DemoViewModel : ViewModel() {
private val api = MyService()
// 通常配合LiveData、StateFlow这些可感知对象为界面提供状态。
private val _uiState = MutableLiveData("")
val uiState: LiveData<String> = _uiState
// 使用SharedFlow为界面提供事件回调
private val _uiEvent = MutableSharedFlow<DemoEvent>()
val uiEvent = _uiEvent.asSharedFlow()
fun reqData(param: String) {
// ViewModel能自动处理协程scope的生命周期
viewModelScope.launch(Dispatchers.IO) {
_uiEvent.emit(DemoEvent.Loading)
try {
val data = api.reqData(param)
_uiState.postValue("rsp: $data")
} finally {
_uiEvent.emit(DemoEvent.Completed)
}
}
}
}
基本用法
在androidx的Activity中使用:
class ViewModelDemoActivity : AppCompatActivity() {
private lateinit var viewModel: DemoViewModel
// viewmodel的ktx扩展库中提供了委托方式获取viewmodel实例
// private val viewModel: DemoViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model_demo)
// ViewModelProvider是获取viewmodel的基础工具,它需要一个ViewModelStoreOwner实例
// 这个实例就是用来存储和管理viewmodel的,androidx的ComponentActivity
// 实现了这个接口,因此可以直接使用AppCompatActivity来初始化ViewModelProvider。
viewModel = ViewModelProvider(this).get(DemoViewModel::class.java)
// 监听界面状态以及事件,并做出响应
viewModel.uiState.observe(this) {
Log.d(TAG, "received response: $it")
}
viewModel.uiEvent
.onEach { showLoading(it == DemoEvent.Loading) }
.launchIn(lifecycleScope)
viewModel.reqData(param)
}
}
在androidx的Fragment中使用:
class DemoFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// jetpack fragment也实现了ViewModelStoreOwner接口,因此也可以用于获取和管理viewmodel
val selfViewModel = ViewModelProvider(this).get(FragmentViewModel::class.java)
}
override fun onAttach(context: Context) {
super.onAttach(context)
// 可以获取到Activity或者其它Fragment的ViewModel,只需要在构造ViewModelProvider
// 时传递了对应的ViewModelStoreOwner。这个实例和DemoActivity
// 中获取到的是同一个实例,因此你可以通过这个实例实现和Activity的通信。
val parentViewModel = ViewModelProvider(requireActivity())
.get(DemoViewModel::class.java)
}
}
SavedStateHandle
SavedStateHandle主要用于在进程销毁-重建过程中恢复数据,它可以将数据持久化到存储中,并在重建后恢复数据。
class DemoViewModel(val savedState: SavedStateHandle) : ViewModel() {
// 可以使用getLiveData将要获取的数据转为LiveData
private val _savedData = savedState.getLiveData<String>(DATA_KEY)
val savedData: LiveData<String> = _savedData
fun saveData(data: String) {
savedState[DATA_KEY] = data
}
fun readData(): String? {
// 也可以直接获取
return savedState[DATA_KEY]
}
companion object {
private const val DATA_KEY = "data"
}
}
AndroidViewModel
有时候ViewModel中可能会需要使用到Android Context(获取文本、颜色等资源),此时可以使用AndroidViewModel,它提供了一个getApplication()方法,可以很方便的获取上下文实例。使用方式如下:
class DemoViewModel(application: Application)
: AndroidViewModel(application) {
fun getString() = application.getString(R.string.hint_txt)
}
带参数的ViewModel
前面几个小节我们都假定了使用androidx的组件作为ViewModelStoreOwner来构造ViewModelProvider。这些androidx的组件会帮助我们自动提供ViewModel所依赖的SavedStateHandle和Application 。
然而,当我们使用自定义ViewModelStoreOwner时,或者想向ViewModel传递其它类型的参数时,就需要自定义ViewModeProvider.Factory了。
假如我们有如下ViewModel,它需要接收一个Repository作为参数:
class MyViewModel(
private val myRepository: MyRepository
) : ViewModel() { }
为了实例化MyViewModel,我们需要再定义一个Factory,然后在create方法中获取依赖对象,构造ViewModel实例:
val factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(
m

本文介绍了Android Jetpack中的ViewModel,它可承载业务逻辑、保存屏幕状态,在配置变更后持久保留数据。阐述了其优势,如持久保持界面状态、限定异步工作作用域等,还介绍了使用方法,包括基本用法、SavedStateHandle等,并对其获取过程、生命周期管理等原理进行了分析。
最低0.47元/天 解锁文章
2849

被折叠的 条评论
为什么被折叠?



