而不是
val accountViewModel = AccountFactory().create(AccountViewModel::class.java)
class AccountFactory: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class): T {
if (modelClass.isAssignableFrom(AccountViewModel::class.java)){
return AccountViewModel() as T
}
throw IllegalArgumentException(“Unknown ViewModel class”)
}
}
Fragment 间数据共享
Activity 中的两个或更多 Fragment 互相通信是一种很常见的需求。假设你有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
可以使用 ViewModel
对象解决这个常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel
来处理此类通信,代码如下:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the ‘by activityViewModels()’ Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the ‘by activityViewModels()’ Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer { item ->
// Update the UI
})
}
}
这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各种获取 ViewModelProvider
时,他们会收到相同的SharedViewModel
实例(其范围限定为该 Activity)。
此方法具有以下优势:
-
Activity 不需要执行任何操作,也不需要对此通信有任何了解。
-
除了
SharedViewModel
约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。 -
每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
ViewModel 的生命周期
ViewModel
对象存在的时间范围是获取ViewModel
时传递给 VIewModelProvider 的 Lifecycle。也就是上面我们调用的代码:
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
这里的this
是Activity
实例对象,因为我们的Activity
实现了 Lifecycle
的关联。ViewModel
将一直留在内存中,直到限定其存在时间范围的 Lifecycle
永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
下图是 Activity 在屏幕旋转而后结束时所处的各种生命周期状态。该图还在关联 Activity 生命周期的旁边显示了 ViewModel
的生命周期。这些基本状态同样适用于 Fragment 的生命周期。
通常是在系统首次调用 Activity 对象的 onCreate()
时请求ViewModel
。ViewModel
存在的时间范围是从首次请求ViewModel
直到 Activity 完成并销毁。
源码分析
在分析源码前,我先提出两个问题,ViewModel 是如何做到让数据可在发生屏幕旋转等配置更改后继续留存(也就是说ViewModel 实例依然存在)?Fragment 之间是如何通过 ViewModel 共享数据的?
先来看看ViewModel
类
public abstract class ViewModel {
···
private volatile boolean mCleared = false;
/**
-
This method will be called when this ViewModel is no longer used and will be destroyed.
-
-
It is useful when ViewModel observes some data and you need to clear this subscription to
-
prevent a leak of this ViewModel.
*/
//该方法将会在 ViewModel 被清除时调用,可以在这个方法里做一些取消注册,防止内存泄漏
@SuppressWarnings(“WeakerAccess”)
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
···
onCleared();
}
···
}
ViewModel 只是一个抽象类,clear()
方法会在 ViewModel 被清除时调用有,用户可以通过重写 onCleared()
方