一、基本概念
1、LiveData
1.1 LiveData 简介
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。
2、LiveData 优势
- 确保界面符合数据状态LiveData 遵循观察者模式。
当底层数据发生变化时,LiveData 会通知 Observer 对象。可以整合代码以在这些 Observer 对象来更新界面。这样一来,无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),它不会接收任何 LiveData 事件。
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。
2、ViewModel
2.1 ViewModel简介
Android Jetpack的一部分,用于为界面准备数据
2.1 ViewModel 优势
- 让应用以注重生命周期的方式存储和管理界面相关的数据
- 让数据可在发生屏幕旋转等配置更改后继续留存
- 从界面控制器逻辑中分离出视图数据所有权,使数据和试图操作更容易且更高效
二、基础使用
1、添加依赖
allprojects {
repositories {
google()
jcenter()
}
}
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
2、代码实战
2.1 创建ViewModel类
class NameTestViewModel:ViewModel() {
val userName:MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
}
2.2 Activity中使用
- 获取ViewModel实例
private val mViewModel: NameTestViewModel by viewModels()
- 创建观察者并为viewModel添加观察者
// 创建observer
val contentObserver = Observer<String> { content ->
// 数据变更更新ui
tvLiveDataContent.text = content
}
// 添加观察者
mViewModel.userName.observe(this, contentObserver)
- LiveData 设置、更新、转换数据
// 设置数据
btnSetData.setOnClickListener {
// mViewModel.userName.value = "张三"
mViewModel.userName.postValue("张三")
}
// 更新数据
btnChangeData.setOnClickListener {
mViewModel.userName.value = "李四"
}
// 转换数据
mapDataFunction()
}
- LiveData 转换数据Map、SwitchMap
private fun mapDataFunction() {
val mapString=MutableLiveData<String>()
mapString.postValue("数据")
// 转换数据
btnMapData.setOnClickListener {
val mapObserver = Transformations.map(mapString) { userName ->
"我是转换后的 $userName"
}
mapObserver.observe(this){
tvLiveDataContent.text = it // text:我是转换后的数据
}
}
}
- switchMap 经过switchMap转换后必须有返回值,返回值为liveData类型数据
private fun switchMapData() {
val switchMapString=MutableLiveData<String>()
switchMapString.value="switchMapData"
val switchMapStringTwo=MutableLiveData<String>()
switchMapStringTwo.postValue("switchMapDataTwo")
btnSwitchMapData.setOnClickListener {
// 经过switchMap转换后必须有返回值,返回值为liveData类型数据
val switchMapObserver=Transformations.switchMap(switchMapString){
// 修改数据
switchMapStringTwo.postValue(it)
// 通过switchMap将switchMapStringTwo作为返回值
switchMapStringTwo
}
switchMapObserver.observe(this){
tvLiveDataContent.text = it // text:switchMapData
}
}
}
- LiveData mediatorLiveData 合并多个数据源
private fun mediatorLiveData() {
val sourceString = MutableLiveData<String>()
sourceString.value = "sourceLiveDataOne"
val sourceStringTwo = MutableLiveData<String>()
sourceStringTwo.postValue("sourceLiveDataTwo")
val mediatorLiveData = MediatorLiveData<String>()
// 添加数据源
mediatorLiveData.addSource(sourceString) {
mediatorLiveData.value = it
}
mediatorLiveData.addSource(sourceStringTwo) {
mediatorLiveData.value = it
}
// 更新数据
btnMediatorLiveData.setOnClickListener {
mediatorLiveData.observe(this) {
tvLiveDataContent.text = it // text: sourceLiveDataTwo
}
}
}
使用mediatorLiveData的addSource添加多个数据源,按照执行先后顺序执行,后执行的会覆盖先执行的数据
3、ViewModel 补充
3.1 viewModel生命周期
-
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。 -
ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失 -
对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时 -
activity中ViewModel生命周期
3.2 Fragment间使用ViewModel共享数据
- 创建两个Fragment
- 获取viewModel实例
- 设置监听
- 更新ui
class FirstFragment : Fragment() {
// 获取viewModel
private val model:DataTestViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return layoutInflater.inflate(R.layout.layout_fragment_first, container, false) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 设置观察者
model.userName.observe(viewLifecycleOwner, Observer {
// 更新ui
tvContent.text=it
})
}
}
class SecondFragment:Fragment() {
private val model: DataTestViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return layoutInflater.inflate(R.layout.layout_fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.userName.observe(viewLifecycleOwner, Observer {
tvContent.text=it
})
}
}
- 将fragment添加到activity中
private fun addFragment() {
val firstFragment=FirstFragment()
val secondFragment=SecondFragment()
supportFragmentManager.beginTransaction()
.add(R.id.flOne,firstFragment)
.add(R.id.flTwo,secondFragment)
.commitAllowingStateLoss()
}
因为两个fragment共享ViewModel,同时实现了监听,所有当viewmodel中数据发生变化,两个fragment都能收到响应,实现ui更新
3.3 协程与ViewModel一起使用
- 添加KTX依赖
// ktx
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
- 结合ViewModelScope使用
// ViewModelScope
init {
viewModelScope.launch {
userName.value="defaultValue"
}
}
- Fragment中结合LifecycleScope使用
// lifecycleScope 执行耗时操作
btnLifecycleScope.setOnClickListener {
viewLifecycleOwner.lifecycleScope.launch {
delay(100)
// 修改数据
model.userName.postValue("lifecycleScope")
}
}
- 可重启生命周期感知型协程
在Fragment的onViewCreated()中监听生命周期变化并打印日志
// 可重启生命周期感知型携程
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.whenCreated {
Log.d(TAG,"lifeCycle onCreated")
}
viewLifecycleOwner.whenStarted {
Log.d(TAG,"lifeCycle onStarted")
}
viewLifecycleOwner.whenResumed {
Log.d(TAG,"lifeCycle onResume")
}
}
D/FirstFragment: lifeCycle onCreated
D/FirstFragment: lifeCycle onStarted
D/FirstFragment: lifeCycle onResume
可见,我们通过viewLifecycleOwner观察到了fragment生命周期的变化。
- 挂起生命周期感知型协程
即使 CoroutineScope 提供了适当的方法来自动取消长时间运行的操作,在某些情况下,可能需要暂停执行代码块(除非 Lifecycle 处于特定状态),手动在finally释放资源
private fun hangUpScope() {
viewLifecycleOwner.lifecycleScope.launch {
try {
whenStarted {
var success = false
// 模拟携程执行1s耗时操作
withContext(Dispatchers.IO) {
delay(1000)
success = true
}
if (success) {
model.userName.postValue("success")
}
}
}finally {
// finally 中释放资源
if (lifecycle.currentState>=Lifecycle.State.STARTED){
releaseSource()
}
}
}
}
// 释放资源
private fun releaseSource() {
Log.d(TAG, "release source")
}
D/FirstFragment: release source
- 结合liveData与协程并通过emit将数据发送出去
// 延迟1s 协程中模拟网络请求并通过emit将数据发送出去
val liveDataItem= liveData {
delay(1000)
val data="John"
emit(data)
}
model.liveDataItem.observe(viewLifecycleOwner, Observer {
Log.d(TAG,it)
})
D/FirstFragment: John
- 使用 liveData 构建器函数调用 suspend 函数,并将结果作为 LiveData 对象传送
- 使用liveData在协程中处理数据,并通过emit将结果发出
- viewModel中代码
// 延迟1s 协程中模拟网络请求并通过emit将数据发送出去
val liveDataItem= liveData {
delay(1000)
val data="John"
emit(data)
}
val resultBean= liveData {
emit(ResultBean(ResultBean.STATUS_LOADING))
try {
delay(2000)
emit(ResultBean(ResultBean.STATUS_SUCCESS))
}catch (e:Exception){
emit(ResultBean(ResultBean.STATUS_FAIL))
}
}
private fun liveDataWithRoutineCombination() {
model.liveDataItem.observe(viewLifecycleOwner, Observer {
Log.d(TAG,it)
})
model.resultBean.observe(viewLifecycleOwner, Observer {
when(it.status){
ResultBean.STATUS_LOADING->{
Log.d(TAG, "resultBean loading")
}
ResultBean.STATUS_SUCCESS->{
Log.d(TAG, "resultBean success")
}
ResultBean.STATUS_FAIL->{
Log.d(TAG, "resultBean fail")
}
}
})
}
D/FirstFragment: resultBean loading
D/FirstFragment: John
D/FirstFragment: resultBean success
- ViewModel 中使用SavedStateHandle保存状态
在onSaveInstance中保存数据,并在下次重启时,通过savedStateHandle读取数据,类似我们在onSaveInstance()中保存数据,在onCreate()中通过读取bundle恢复数据
- 支持的数据类型
保留在 SavedStateHandle 中的数据将作为 Bundle 与 Activity 或 Fragment 的 savedInstanceState 的其余部分一起保存和恢复
如果该类没有扩展上述列表中的任何一项,则应考虑通过添加 @Parcelize Kotlin 注解或直接实现 Parcelable 来使该类变为 Parcelable 类型或者实现Serializable实现序列化
- 代码实例
ViewModel注意构造方法中添加SavedStateHandle
class DataViewModelTwo(private val savedStateHandle: SavedStateHandle):ViewModel(){
private val TAG="tag"
// 设置query数据
fun setQuery(query: String) {
if (savedStateHandle.contains("query")) {
Log.d(TAG, "contains query==== ${savedStateHandle.get<UserBean>("query")}")
}else{
Log.d(TAG,"保存数据")
savedStateHandle["query"] = UserBean(userName = query,userAge = 10)
}
}
// 获取数据
fun getSavedValue(): UserBean? {
return savedStateHandle.get<UserBean>("query")
}
}
private val model2: DataViewModelTwo by activityViewModels()
- Fragment的onSaveInstance中保存数据
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
model2.setQuery("啦啦啦啦")
}
- 在Fragment中onResume()方法中模拟获取数据
override fun onResume() {
super.onResume()
// saveData
saveDataFunction()
}
private fun saveDataFunction() {
Log.d(TAG, "query==== ${model2.getSavedValue()}")
}
D/tag: query==== null
D/tag: 保存数据
D/tag: query==== UserBean(userName=啦啦啦啦, userAge=10)
通过上面的例子我们就实现了viewModel保存数据的效果
- 参考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel?hl=zh_cn https://developer.android.google.cn/topic/libraries/architecture/livedata?hl=zh_cn https://developer.android.google.cn/topic/libraries/architecture/viewmodel-savedstate?hl=zh_cn
|