当前位置: 首页 > news >正文

Android的LiveData

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 activity、fragment 或 service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者

若观察者(Observer)的生命周期处于STARTEDRESUMED状态,则LiveData会认为该Observer处于活跃状态。LiveData只会将更新通知给活跃的Observer。

您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 activity 和 fragment 特别有用,因为它们可以放心地观察 LiveData 对象,而不必担心泄露(当 activity 和 fragment 的生命周期被销毁时,系统会立即退订它们)。

使用LiveData的优势

确保界面符合数据状态:LiveData遵循观察者模式。当底层数据发生变化,LiveData会通知Observer对象,此时我们可以写入逻辑,以在Observer中更新界面。这样一来,我们就不用每次在数据发生变化时更新界面,因为Observer会完成这一内容;

不会发生内存泄漏:Observer绑定到LifeCycle对象,并在其关联的生命周期结束后自动清理,这一点与ViewModel类似;

不会因为Activity停止而导致崩溃:若Observer的生命周期处于非活跃状态(如返回对战的activity),它就不会接受LiveData事件;

不需要手动处理生命周期:界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化;

使用LiveData对象

当更新存储在LiveData对象中的值时,它会触发所有已注册的Observer(只要它所附加的Life cycleOwner处于活跃状态)。

创建

LiveData对象通常存储在ViewModel对象中,并可以使用getter方法访问:

class NameViewModel : ViewModel() {// LiveData可用于任何类型的数据,此处创建一个String类型的LiveData对象val currentName: MutableLiveData<String> by lazy {//使用lazy懒加载,当需要使用再创建MutableLiveData<String>()}// ViewModel的剩余部分...
}

存储在ViewModel中、而不是activity或fragment的原因是:

  • 避免activity和fragment过于庞大。我们使用MVVM架构的原因之一,就是为了使这些界面控制器只负责显示数据,而不存储数据;
  • 将LiveData实例与特定的activity或fragment实例分离开,使得LiveData对象在配置更改后仍然存在。

观察

大多数时候,从组件onCreate方法中开始观察LiveData对象,以确保系统不会从onResume方法中进行冗余调用,并确保activity或fragment变为活跃状态后具有可以立即显示的数据。一旦组件处于STARTED状态,就会从它观察的LiveData接受最新的值。

另外,除了LiveData在发生数据更改时会发送更新,Observer从非活跃状态变为活跃状态时也会收到更新。但是,如果Observer第二次发生这样的状态改变,则只有上次的改变会收到更新。

class NameActivity : AppCompatActivity() {private val model: NameViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 其他初始化activity的内容...// 创建观察ui更新的observerval nameObserver = Observer<String> { newName ->// 更新ui,此处是一个TextViewnameTextView.text = newName}// 观察LiveData, 将此activity作为LifecycleOwner和observermodel.currentName.observe(this, nameObserver)}
}

我们也可以简写这一部份:

class NameActivity : AppCompatActivity() {private val model: NameViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 其他初始化activity的内容...// 简写nameTextView.text = model.currentName.observeAsState()}
}

在传递nameObserver参数的情况下调用observe方法后,系统会立即调用onChanged方法,从而提供mCurrentName中存储的最新值。若LiveData对象上为在mCurrentName中设置值,系统不会调用onChanged方法。

更新

LiveData没有公开可用的方法来更新存储的数据。如果需要修改存储在LiveData对象中的值,需要重写MutableLiveData中的setValue方法或postValue方法。

通常情况下会在ViewModel中使用MutableLiveData,然后ViewModel只会向观察者公开不可变的LiveData对象。设置观察者关系之后,我们就可以更新LiveData对象的值:

button.setOnClickListener {val anotherName = "John Doe"model.currentName.setValue(anotherName)
}

在主线程中我们使用setValue方法更新数据,而在工作器线程中,我们可以改用postValue方法来更新Livedata对象。

扩展LiveData 

如果观察者的生命周期处于STARTED或RESUMED状态,则LiveData会认为该观察者处于活跃状态,以下是扩展LiveData类的例子:

//价格监听器
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {private val stockManager = StockManager(symbol)private val listener = { price: BigDecimal ->value = price}//当LiveData对象具有活跃观察者时,会调用此方法override fun onActive() {//从此方法开始观察股价更新stockManager.requestPriceUpdates(listener)}//当LiveData对象没有活跃观察者时,会调用此方法override fun onInactive() {//断开StockManager服务stockManager.removeUpdates(listener)}
}

接下来我们使用重写后的StockLiveData类:

public class MyFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)val myPriceListener: LiveData<BigDecimal> = ...myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->// Update the UI.})}
}

组件如activity、fragment在初始化时构建了自己的LifecycleOwener。此处的observe方法将与Fragment视图关联的LifecycleOwner作为第一个参数传递,这样做表示此观察者已绑定到与其所有者关联的Lifecycle对象(即组件,在此处是MyFragment),这意味着;

  • 如果Lifecycle对象未处于活跃状态,即使值发生更改,也不会调用观察者;
  • 销毁Lifecycle对象后,会自动移除观察者。

同时LiveData对象具有生命周期感知能力,意味着我们可以在多个activity、fragment和service之间共享这些对象。

我们也可以将StockLiveData实现为一个单例(companion object):

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {private val stockManager: StockManager = StockManager(symbol)private val listener = { price: BigDecimal ->value = price}override fun onActive() {stockManager.requestPriceUpdates(listener)}override fun onInactive() {stockManager.removeUpdates(listener)}companion object {private lateinit var sInstance: StockLiveData@MainThreadfun get(symbol: String): StockLiveData {sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)return sInstance}}
}

为什么使用了单例?以StockLiveData为例,单例的好处是在使用时不需要再创建一个StockLiveData的实例,而是直接引用它的方法:

class MyFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->// Update the UI.})}

相关文章:

  • 机器学习理论知识学习
  • 化学分子Mol2文件格式与使用注意事项
  • vue-element-admin如何绕开系统的请求的路由,使用静态路由
  • 【GameFramework框架内置模块】4、内置模块之调试器(Debugger)
  • https://htmlunit.sourceforge.io/
  • SpringBoot快速入门(黑马学习笔记)
  • Vue.js+SpringBoot开发超市商品管理系统
  • 基于Springboot + Vue 母婴商城系统
  • SQL库操作
  • Mac使用K6工具压测WebSocket
  • uniapp中在app中清除缓存功能
  • 分布式任务调度的几种实现(Redis实现分布式锁 MySQL实现任务调度 负载均衡)
  • 大语言模型的开山之作—探秘GPT系列:GPT-1-GPT2-GPT-3的进化之路
  • MATLAB环境下一种改进的瞬时频率(IF)估计方法
  • YOLO算法改进Backbone系列之:EfficientViT
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • HTTP 简介
  • PHP的Ev教程三(Periodic watcher)
  • SegmentFault 2015 Top Rank
  • storm drpc实例
  • webpack项目中使用grunt监听文件变动自动打包编译
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 不上全站https的网站你们就等着被恶心死吧
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • - 概述 - 《设计模式(极简c++版)》
  • 给github项目添加CI badge
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 开发基于以太坊智能合约的DApp
  • 浅谈sql中的in与not in,exists与not exists的区别
  • (2)(2.10) LTM telemetry
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (补)B+树一些思想
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (十八)三元表达式和列表解析
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET Micro Framework 4.2 beta 源码探析
  • .NetCore 如何动态路由
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • /dev/sda2 is mounted; will not make a filesystem here!
  • @NestedConfigurationProperty 注解用法
  • @RestControllerAdvice异常统一处理类失效原因
  • [android] 练习PopupWindow实现对话框
  • [android] 切换界面的通用处理
  • [C++] cout、wcout无法正常输出中文字符问题的深入调查(1):各种编译器测试
  • [IDF]摩斯密码
  • [IE编程] WebBrowser控件的多页面浏览(Tabbed Browsing)开发接口
  • [LeetCode] Ransom Note 赎金条
  • [Mac软件]Goldie App v2.2 Mac黄金比例设计工具
  • [PHP]禅道项目管理软件ZenTaoPMS源码包 v16.4
  • [python]tkinker的GUI应用执行耗时长的任务
  • [RK3568][Android12.0]--- 系统自带预置第三方APK方法
  • [VulnHub靶机渗透] Nullbyte