Jetpack--ViewModel
ViewModel 速描
资料来源:
https://developer.android.com/topic/libraries/architecture/viewmodel#implement
更新
1
20.02.24 初始化
导语
- 还是官方文档总结….
- 导入不涉及了…
基础
先来看看引入 ViewModel 要解决什么问题吧?
- 在 Activity/Fragment 中堆积了太多的逻辑,太厚了,不利于开发和单元测试.
- 在 Activity/Fragment 重建以后的数据缓存,少量的数据可以通过 onSaveInstanceState() .但是数据量大或数据并不适合序列化,就无能为力了.
- 涉及到业务逻辑总是会有异步调用,但 Activity/Fragment 生命周期受系统控制,处理异步调用的生命周期异常复杂.
ViewModel是如何解决的呢?
- google 官方推荐的是 ViewModel 持有 LiveData,Activity/Fragment 中仅涉及一些初始化.业务逻辑等全部扔到 ViewModel 中.
- LiveData 有数据缓存,且 ViewModel 的生命周期要比 Activity/Fragment 长的多,每次重建后直接到 ViewModel 取数据即可.
- 异步调用已经在 ViewModel 中了,配合 rxjava 或者 kotlin 的协程等可以比较轻松的处理异步调用.
创建 ViewModel 实例
需要继承 ViewModel 类,一般 LiveData 声明在 ViewModel 中.
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}Activity 访问
1
2
3
4
5
6
7
8
9
10
11
12class 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
})
}
}- ViewModel 的生命周期要长的多,即使 Activity 被重建,得到的始终都是一个 ViewModel 实例.
严重警告!: ViewModel 中绝对不能引用任何视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类.简而言之 ViewModel 不能对视图有任何感知.
如果 ViewModel 需要 Application 上下文(例如,为了查找系统服务),它可以扩展 AndroidViewModel 类并设置用于接收 Application 的构造函数,因为 Application 类会扩展 Context.(这段有点没看懂,是需要上下文可以接收 Application 吗?)
生命周期
- ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle.ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时.
- 以 Activity 为例:
ViewModel 的长生命周期和对 Activity 的单例性质,额外解决了依附于同一 Activity 的 Fragment 互相通信的问题.
对于 Activity 而言 ViewModel 是单例的,那么就可以在所有 Fragment 中获取到这个单例的 ViewModel,这个过程对 Activity 是无感的,利用 ViewModel 获取到数据.并且 Fragment 的生命周期不会影响到 ViewModel.
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38class 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: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//获取ViewModel
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)
//获取ViewModel
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
model.selected.observe(this, Observer<Item> { item ->
// Update the UI
})
}
}
替换加载器
- Android 中使用数据库经常有 CursorLoader 之类的加载器使应用界面中的数据与数据库保持同步.ViewModel + Room + LiveData 一起使用可替换加载器.
- ViewModel 确保数据在设备配置更改后仍然存在.Room 在数据库发生更改时通知 LiveData,LiveData 进而使用修订后的数据更新界面.(这个也是 jetpack 推荐的架构)
与 Kotlin 协程协作.(大概是给协程挂上”生命周期”)
ViewModelScope: 每个 ViewModel 都定义了 ViewModelScope.如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消.对于限定在 ViewModel 内的活动非常实用.
例:
1
2
3
4
5
6
7class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
结语
- 文档用到的暂时总结到此.
- 下一篇是 Room .