一、ViewModel
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
1.1 简单实现
class MyViewModel : ViewModel() {
val users = MutableLiveData<List<User>>()
fun loadUsers() {
// 执行异步操作以获取用户列表
users.value = userDao.getUsers()
}
}
注意:ViewModel 绝不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类。
可以从 Activity 访问该列表,如下所示:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val viewModel: MyViewModel by viewModels()
//val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
viewModel.loadUsers()
viewModel.users.observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
部分代码用到了Android KTX
1.2 ViewModel 的生命周期
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从您首次请求 ViewModel 直到 Activity 完成并销毁。
1.3 在 Fragment 之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。我们可以利用ViewModel实现Activity 中多个Fragment 共享数据。这多个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:
class SharedViewModel : ViewModel() {
val share:String = ""
}
class MasterFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
val viewModel: SharedViewModel by activityViewModels()
//val viewModel= ViewModelProvider(requireActivity()).get(SharedViewModel ::class.java)
val btn: Button = findViewById(R.id.button)
btn.setOnClickListener {
// Update the UI
viewModel.share.value = "share data"
}
}
}
class DetailFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
val viewModel: SharedViewModel by activityViewModels()
//val viewModel= ViewModelProvider(requireActivity()).get(SharedViewModel ::class.java)
val textView: TextView = findViewById(R.id.textView)
textView.text = viewModel.share
}
}
这两个 Fragment 通过by activityViewModels()或者ViewModelProvider(requireActivity()).get(SharedViewModel ::class.java)的方式获取SharedViewModel实例的时候传入的都是依赖的同一个Activity,所以这两个 Fragment 会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。
总结:在获取范围限定相同的 ViewModel,就会收到相同的ViewModel实例。
这个也能实现同一导航图内共享ViewModel ,可参阅Android Navigation 组件(进阶篇)
1.4 与Kotlin 协程使用
关于协程的介绍可参阅:Kotlin协程详解
文中介绍的内置协程范围包含在viewmodel架构组件的 KTX 扩展程序中。请务必在使用时添加相应的依赖项:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
可以通过 ViewModel 的 viewModelScope 属性访问 ViewModel 的 CoroutineScope,如以下示例所示:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。
二、LiveData
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
在 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData 对象而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。
LiveData与MutableLiveData的其实在概念上是一模一样的。唯一几个的区别如下:
MutableLiveData的父类是LiveDataLiveData在实体类里可以通知指定某个字段的数据更新.MutableLiveData则是完全是整个实体类或者数据类型变化后才通知。不会细节到某个字段
2.1 简单实现
LiveData 是一种可用于任何数据的封装容器,其中包括可实现 Collections 的对象,如 List。LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 方法进行访问,如以下示例中所示:
class NameViewModel : ViewModel() {
// Create a LiveData with a String
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
// Rest of the ViewModel...
}
在大多数情况下,我们是在 onCreate() 方法给 LiveData 对象添加观察者
class NameActivity : AppCompatActivity() {
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
private val viewModel: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val textView: TextView = findViewById(R.id.textView)
viewModel.currentName.observe(this, Observer<String> { newName ->
// Update the UI, in this case, a TextView.
textView.text = newName
})
}
}
我们可以通过setValue(T) 和 postValue(T) 方法,修改存储在 LiveData 对象中的值并通知观察者。
button.setOnClickListener {
val anotherName = "John Doe"
viewModel.currentName.value = anotherName
}
区别:
setValue():只能在主线程中调用postValue():可以在任何线程中调用
2.2 与Room 使用
Room 是官方推荐使用的数据库,了解详情可参阅:Android数据库框架——Room框架的使用
Room 数据库支持返回 LiveData 对象的可观察查询。可观察查询属于数据库访问对象 (DAO) 的一部分。我们可以通过查询操作直接返回LiveData类型数据
@Query("select * from user where userId = :id")
fun getUserById(id: Long): LiveData<User>
2.2 与Kotlin 协程使用
关于协程的介绍可参阅:Kotlin协程详解
文中介绍的内置协程范围包含在LiveData 架构组件的 KTX 扩展程序中。请务必在使用时添加相应的依赖项:
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
在以下示例中,loadUser() 是在其他地方声明的 suspend 函数。 您可以使用 liveData 构建器函数异步调用 loadUser(),然后使用 emit() 来发出结果:
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
当 LiveData 变为活动状态时,代码块开始执行;当 LiveData 变为非活动状态时,代码块会在可配置的超时过后自动取消。如果代码块在完成前取消,则会在 LiveData 再次变为活动状态后重启;如果在上次运行中成功完成,则不会重启。注意:代码块只有在自动取消的情况下才会重启。如果代码块由于任何其他原因(例如,抛出 CancelationException)而取消,则不会重启。
您还可以从代码块中发出多个值。每次 emit() 调用都会暂停执行代码块,直到在主线程上设置 LiveData 值。
val number: LiveData<String> = liveData {
delay(1000)
emit("1")
delay(1000)
emit("2")
delay(1000)
emit("3")
delay(1000)
emit("4")
delay(1000)
emit("5")
}
class NameActivity : AppCompatActivity() {
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
private val viewModel: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val textView: TextView = findViewById(R.id.textView)
viewModel.currentName.observe(this, Observer {
textview.text = it
})
}
}
就能看到如下效果:
每次调用 emit() 或 emitSource() 都会移除之前添加的来源。