一、ViewModel要解决的问题
在ViewModel出现之前,无论是MVP、MVC还是MVVM,我们都需要自己处理数据的状态保存问题。因为展示给用户的页面(Activity、Fragment)随时可能会被系统销毁,比如用户旋转手机屏幕或者手机内存不足等等。等页面重建时,我们需要重新获取数据进行展示。对于简单的数据,我们可以通过使用onSaveInstanceState()来恢复,而不适合大量的列表数据。同时,我们获取数据通常时异步请求的,如果这些调用返回前,发生了销毁。我们需要进行额外的取消清理工作,防止内存泄漏。
这些逻辑的处理往往繁琐并且重复,所以Google官方推出了ViewModel来解决这个问题。ViewModel负责为界面准备数据,并在更改配置期间自动保留ViewMode对象,以便它们存储的数据立即可供下一个Activity或Fragment实例使用。
二、实现ViewModel
class MyViewModel : ViewModel {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().alse {
loadUsers()
}
}
fun getUsers(): LiveData<List<Users>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users
}
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
val model = ViewModelProviders.of(this)[MyViewModel::class.java]
model.getusers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
如果重新创建了该Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同。当所有者Activity销毁时,框架会调用ViewModel的onCleared()方法,以便它可以清理资源。
ViewModel对象存在的时间比视图或LifecycleOwners的特定实例存在的时间更长。这还意味着,您可以更轻松地编写覆盖ViewModel的测试,因为它不了解视图和Lifecycle对象。ViewModel对象可以包含LifecycleObservers,如LiveData对象。但是,ViewModel对象绝不能观察对生命周期感知型可观测对象(如LiveData对象)的更改。如果ViewModel需要Application上下文(例如,为了查找系统服务),它可以扩展AndroidViewModel类并设置用于接收Application的构造函数,因为Application类会扩展Context。
三、ViewModel的生命周期
ViewModel对象存在的时间范围是获取ViewModel时传给ViewModelProvider的Lifecycle。ViewModel将一直停留在内存中,直到限定其存在时间范围的Lifecycle永久消失:对于Activity,是在Activity Finish的时候。对于Fragment,是在Fragment Destroy的时候。
您通常在系统首次调用Activity对象的onCreate()方法时请求ViewModel。系统可能会在Activity的整个生命周期内多次调用onCreate(),如在旋转设备时。ViewModel存在的时间范围是从您首次请求ViewModel直到Activity完成并销毁。
四、在Fragment之间共享数据
Activity中的两个或更多Fragment需要互相通信时一种很常见的情况。想象一下主从Fragment的常见情况,假设您有一个Fragment,在该Fragment中,用户从列表中选择一项,还有另一个Fragment,用于显式选定的内容。这种情况不太容易处理,因为这两个Fragment都需要定义某种接口描述,并且所有者Activity必须将两者绑定在一起。此外,这两个Fragment都必须处理另一个Fragment尚未创建或不可见的情况。
可以使用ViewModel对象解决这一常见的难点。这两个Fragment可以使用其Activity范围共享ViewModel来处理此类通信,如以下示例代码所示:
Class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector : Selector
private lateinit var model : ShareViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
class DetailFragment : Fragment() {
private lateinit var model: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
model.selected.observe(this, Observer<Item> {
// Update the UI
})
}
}
}
请注意,这两个Fragment都会检索包含它们的Activity。这样,当两个Fragment各自获取ViewModelProvider时,它们会收到相同的ShardedViewModel实例(其范围限定为该Acitivity)
此方法具有以下优势:
- Activity不需要执行任何操作,也不需要对此通信有任何了解。
- 除了ShareViewModel约定之外,Fragment不需要互相了解。如果其中一个Fragment消失,另一个Fragment将继续照常工作。
- 每个Fragment都有自己的生命周期,而不受另一个Fragment的生命周期的影响。如果一个Fragment替换另一个Fragment,界面将继续工作而没有任何问题。