感谢你的再次光临,欢迎来到Android Architecture Components(ACC)系列文章。上篇文章我们一起讨论了Room,通过Room我们能够方便的操作App的数据库。如果你的App对本地数据库有所依赖的话,Room你值得拥有。

今天这篇文章继续上篇文章的步伐,让我们一起来全面了解ACC另一强大的组件LiveData。相信你马上会喜欢上她!???

简述

LiveData是一种可观测数据容器,它会在数据变化时通知观测器,以便更新页面;同时它具备生命感知能力,可以实时观察Activity/Fragment的生命周期状态。

既然它是可观察数据容器与具备生命感知能力,那么它的优点也很明显,可以归纳与以下几点

  1. 确保ui跟随数据更新
  2. 具备生命感知能力从而减少内存泄露
  3. 防止异常crashs
  4. 无需管理绑定者的生命周期
  5. ui获取的数据都是最近最终的更新数据

使用场景

当我们要监听某一个数据的变化时,LiveData将大显身手。例如界面数据的更新,当数据发生变化时,我们要通知界面进行更新ui,这时我们可以使用LiveData在当前Activity/Fragment中对该数据注册一个观察者,实时监听数据的任何改动。每一次改动LiveData都会发送通知给观察者。

另一方面,LiveData感知界面的生命周期,所以只有在界面生命周期的STARTED或者RESUMED状态才会通知观察者。如果你一直处于后台且数据一直在变化,LiveData是不会发生通知,只有在界面再一次回到前台,这时LiveData才会发生通知且只会发送一次,数据的更新取的是最后一次的变化数据。这样可以有效的避免内存泄露与ui不存在时导致的NullPointerException

使用

首页我们需要在我们的app下的build.gradle中添加如下依赖代码

dependencies {def lifecycle_version = "1.1.1"// ViewModel and LiveDataimplementation "android.arch.lifecycle:extensions:$lifecycle_version"// alternatively - just LiveDataimplementation "android.arch.lifecycle:livedata:$lifecycle_version"annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
}

然后我们就能正式使用LiveData,看如下代码:

class ContactsViewModel(application: Application, private val defTitle: String = "Contacts") : AndroidViewModel(application) {val message: MutableLiveData<String> by lazy { MutableLiveData<String>() }val contactsList: MutableLiveData<List<ContactsModel>> = MutableLiveData()fun getContacts(refresh: Boolean): LiveData<List<ContactsModel>> {message.value = ""if (refresh) {getDataFromRemote()} else if (contactsList.value == null || contactsList.value?.size ?: 0 <= 0) {message.value = "数据请求中,请稍后!"if (mLocalData.isEmpty()) {getDataFromLocal()}}return contactsList}private fun getDataFromLocal() {val runnable = Runnable {val dao = mContactsDao.getAllContacts()if (dao.isNotEmpty()) {contactsList.postValue(dao)} else {getDataFromRemote()}}mExecutors.disIoExecutor.execute(runnable)}private fun getDataFromRemote() {Handler().postDelayed({contactsList.value = mRemoteDatamLocalData = mRemoteDatasaveContacts(mRemoteData)Thread(Runnable {title.postValue("Remote Contacts")}).start()message.value = "数据加载完成~"}, MDELAY_MILLIS)}
}    

首先我们使用MutableLiveDat对我们所需要的数据进行了包裹,MutableLiveData它继承与LiveData,暴露了postValue()setValue()方法。一旦MutableLiveData所包裹的数据发生变化,我们可以通过postValue()(asynchronously)与setValue()(synchronously)来设置值与发送通知,告诉观察者数据已经改变。

getDataFromLocal()方法中,我们使用了Room来操作数据库,同时直接通过返回LiveData数据类型的数据,使得Room与LiveData完美结合。

所以我们再来看看观察者的代码:

class ContactsActivity : AppCompatActivity() {private lateinit var mViewModel: ContactsViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_contacts_layout)setupViewModel()}private fun setupViewModel() {mViewModel = ViewModelProviders.of(this, ContactsFactory.getInstance(application))[ContactsViewModel::class.java]//active STARTED、RESUMEDmViewModel.getContacts(true).observe(this,Observer {//todo ...})mViewModel.message.observe(this,Observer {//todo ...})}
}

我们为所需要观察的数据添加了observer方法,该方法第一个参数是LifecyleOwner,以便让LiveData具有生命感知能力,这里要感知的是ContactsActivity,所以传入this即可。第二个参数是一个回调方法,一旦数据发生变化它的onChanged()就会回调,并将数据带回,这样界面就能实时更新数据。

最后由于LiveData是生命感知的所以我们也无需担心他的register/unregister

Extend

我们已经知道LiveData会对处于STATERD或者RESUMED状态进行发送通知,如果该状态下存在observer,由无到有,我们称之为active,反正称之为inactive。如果我们能够知道何时为active与何时为inactive,那么我们就可以实现自己的LiveData。为了解决这个问题,LiveData提供了两个方法,分别为onActive()onInactive()

例如我们想为一个监听器实现生命感知能力,可以进行如下操作

public class StockLiveData extends LiveData<BigDecimal> {private static StockLiveData sInstance;private StockManager mStockManager;private SimplePriceListener mListener = new SimplePriceListener() {@Overridepublic void onPriceChanged(BigDecimal price) {setValue(price);}};@MainThreadpublic static StockLiveData get(String symbol) {if (sInstance == null) {sInstance = new StockLiveData(symbol);}return sInstance;}private StockLiveData(String symbol) {mStockManager = new StockManager(symbol);}@Overrideprotected void onActive() {mStockManager.requestPriceUpdates(mListener);}@Overrideprotected void onInactive() {mStockManager.removeUpdates(mListener);}
}

一旦observer由无到有,那么我们就在onActive()方法中进行监听器的注册。observer由有到无,我们可以在onInactive()中进行注销。这样就可以是我们的监听器具备生命感知能力。避免不必要的内存泄露或者一次crash。同时一旦监听器的回调方法生效时,我们又可以通过LiveData的setValue()来对观察者进行数据的更新。所以观察者的代码如下:

public class MyFragment extends Fragment {@Overridepublic void onActivityCreated(Bundle savedInstanceState) {StockLiveData.get(getActivity()).observe(this, price -> {// Update the UI.});}
}

如果细心的话,可以发现上面的StockLiveData已经实现了监听器共享。我们可以在多个界面中使用StockLiveData进行添加observer。例如在Activity中,只要有一个observer,那么它将一直监听数据的变化。

案例:对于App统计需求,一旦涉及到多个页面间的统计参数传递,可以自定义一个扩展LiveData来全局监听参数的传递与变化。

Transform

在通知观察者数据改变之前,如果你想改变LiveData中的值类型,可以使用Transformations

Transformations.map()

获取原有类型中的某个特定的类型值,可以比喻为解包,可以使用map()方法

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {user.name + " " + user.lastName
});

Transformations.switchMap()

与map对应的是switchMap()方法,这里就是打包。

private LiveData<User> getUser(String id) {...;
}LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

MediatorLiveData

与LiveData相关的还有一个MediatorLiveData,它的作用是:可以同时监听多个LiveData。例如同时监听本地数据与远程数据。

LiveData<List<User>> usersFromDatabase;
LiveData<List<User>> usersFromNetwork;MediatorLiveData<List<User>> usersLiveData = new MediatorLiveData<>();usersLiveData.addSource(usersFromDatabase, newUserList ->usersLiveData.setValue(value));usersLiveData.addSource(usersFromNetwork, newUserList ->usersLiveData.setValue(value));

一旦其中一个发送变化,MediatorLiveData都会发送通知给observer。

是否感觉LiveData很强大呢?那么赶紧行动起来吧,让你的App中数据也具有可观察与生命感知能力。

最后文章中的代码都可以在Github中获取到。使用时请将分支切换到feat_architecture_components

相关文章

Android Architecture Components Part1:Room
Android Architecture Components Part3:Lifecycle
Android Architecture Components Part4:ViewModel

关注

私人博客

Android Architecture Components Part2:LiveData相关推荐

  1. Android Architecture Components 整理

    Android Architecture Components是谷歌在Google I/O 2017发布一套帮助开发者解决Android架构设计的方案. 里面包含了两大块内容: 生命周期相关的Life ...

  2. 基于 Android Architecture Components 的 MVVM 浅析

    0.前言 官方文档永远是最好的学习资料: Android Jectpack Android Jetpack: LiveData 和 Lifecycle 介绍 | 中文教学视频 Android Jetp ...

  3. 浅谈Android Architecture Components

    浅谈Android Architecture Components 浅谈Android Architecture Components 简介 Android Architecture Componen ...

  4. Android Jetpack Components of LiveData 学习笔记

    Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...

  5. Android Architecture Components

    开发者经常面临的问题 Android应用由四大组件构成,各组件可以被独立且无序的调起,用户会在各个App之间来回切换.组件启动后,生命周期会受用户的操作和系统影响,不完全受开发者控制.而由于设备内存问 ...

  6. Android Architecture Components 之 Room 篇

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNfFSo5n-1651804572351)(https://user-gold-cdn.xitu.io/2018/4/ ...

  7. [译] Architecture Components 之 Adding Components to your Project

    [目录] 1. Architecture Components 之 Guide to App Architecture 2. Architecture Components 之 Adding Comp ...

  8. [译] Architecture Components 之 Room Persistence Library

    [目录] 1. Architecture Components 之 Guide to App Architecture 2. Architecture Components 之 Adding Comp ...

  9. Android Jetpack Components of ViewModel 学习笔记

    Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...

最新文章

  1. 王兴和张一鸣和我们的互联网启蒙
  2. SAP WM MIGO移动类型311转库过账后WM层面产生了Posting Change Notice?
  3. java节假日api--关于节假日想到的
  4. 【Android 应用开发】Android 返回堆栈管理 ( 默认启动模式 | 栈顶复用启动模式 | 栈内复用启动模式 | 单实例启动模式 | CLEAR_TOP 标识 )
  5. 安装 | MatlabR2021bMac链接及Matlabx运行图基本运行代码与图像
  6. 通俗易懂,C#如何安全、高效地玩转任何种类的内存之Span
  7. java 返回js_如何基于java或js获取URL返回状态码
  8. 你真的会用 regexp_replace 吗?
  9. MOSS之:DataForm Web Part解析
  10. RocketMQ消息消费源码分析
  11. 201521123045 《Java程序设计》第7周学习总结
  12. java把字符串转为日期_Java程序将字符串转换为日期
  13. 用C写邮箱密码暴力破解器
  14. 清除html中table标签显示的数据,jquery – 数据清除tbody
  15. android微信运动页面开发,微信小程序仿微信运动步数排行(交互)
  16. Android之原始的QQ好友分组实现
  17. 如何成为一名优秀的技术 Leader?(转)
  18. VPS一键测试脚本 / 自带结果导出
  19. python爬取酷狗音乐_Python爬取酷狗音乐
  20. h5 修改title 微信_iH5中级教程:微信必备,为H5设定标题

热门文章

  1. 解读2019华为第001号文件:AI时代软件开发的第一要义是可信
  2. 如何使用charles对Android Https进行抓包
  3. 未来数据中心的选择:宽带多模光纤(WBMMF)
  4. debian 8 网桥
  5. jQuery 中 jQuery(function(){})与(function(){})(jQuery) 的区别
  6. 【转】VC++ MFC文件的移动复制删除更名遍历操作
  7. 【新手教程】如何向App Store提交应用
  8. 数据防泄漏(中文版)
  9. 互联网绑上美国外交政策战车
  10. Android 中文API (91) —— GestureDetector