本文已参与[新人创作礼]活动,一起开启掘金创作之路
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
ViewModel引入见 Lifecycle官方库
LiveData引入见 Lifecycle官方库
Kotlin 扩展模块支持使用多个 AndroidX 依赖项。这些模块的名称后附加了后缀“-ktx”。例如:
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
会变为
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
LiveData简介
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
使用 LiveData 具有以下优势:
确保界面符合数据状态
LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
不会发生内存泄漏
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。 不会因 Activity 停止而导致崩溃 如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
共享资源
您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。如需了解详情,请参阅扩展 LiveData。
使用 LiveData
创建 LiveData 的实例以存储某种类型的数据。这通常在 ViewModel 类中完成。
class MyViewModel:ViewModel() {
val count = MutableLiveData<Int>()
//这种写法避免修改
//private val _count = MutableLiveData<Int>()
//val count: LiveData<Int>
// get() = _count
//在非主线程更新数据
count.postValue(1)
//在主线程更新数据
count.value = 1
}
观察LiveData
class MainActivity : AppCompatActivity(){
private val myViewModel by viewModels<MyViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
myViewModel.count.observe(this, Observer {
binding.btnRandom.text = it.toString()
})
}
}
这样当count的值发生变化的时候就会通知到observe方法。
ViewModel简介
场景一 Activity 中包含用户列表,为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
场景二 对于需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
如果要求Activity也负责从数据库或网络加载数据,那么会使类越发膨胀。为Activity分配过多的责任而不是将工作委托给其他类。Activity分配过多的责任也会大大增加测试的难度。
从Activity中分离出视图数据所有权的操作更容易且更高效。
实现 ViewModel
class MyViewModel:ViewModel() {
var numbers = 0
}
然后您可以在Activity中调用
class MainActivity : AppCompatActivity(){
private val myViewModel by viewModels<MyViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.btnAdd.setOnClickListener {
//viewmodel的数据不跟生命周期走,不管生命周期到了哪一步,viewmodel的数据都存在
//如果重新创建了该 Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同。
// 当所有者 Activity 完成时,框架会调用 ViewModel 对象的 onCleared() 方法,以便它可以清理资源。
//不要往viewmodel中传入context,会导致内存泄露
//不要使用context,请使用AndroidViewModel中的Application
//比如屏幕旋转,viewmodel中的number是不会变化的
binding.btnAdd.text = (++myViewModel.numbers).toString()
}
}
}
ViewModel 的生命周期
您通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从您首次请求 ViewModel 直到 activity 完成并销毁
在 Fragment 之间共享数据
假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。 那么就需要用activityViewModels来实现数据共享了。
创建ViewModel
class MyViewModel:ViewModel() {
val count = MutableLiveData<Int>()
//随机赋值
fun updateCount(){
count.value = (1..10).random()
}
}
创建AFragment 来修改数据
class AFragment : Fragment() {
private lateinit var binding :FragmentABinding
//activityViewModels是通信的关键
//fragment,activity,其他fragment 三者皆可通信
private val myViewModel by activityViewModels<MyViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentABinding.inflate(inflater,container,false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
myViewModel.count.observe(viewLifecycleOwner, Observer {
Log.v("zx","随机$it")
})
//数据数据
binding.btnUpdate.setOnClickListener {
myViewModel.updateCount()
}
}
class BFragment : Fragment() {
private lateinit var binding: FragmentBBinding
private val myViewModel by activityViewModels<MyViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentBBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//当A调用updateCount方法后,这里就会收到回调
myViewModel.count.observe(viewLifecycleOwner, Observer {
Log.v("zx","随机B$it")
})