感谢你的再次光临,欢迎来到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. linux sed 慢,echo/awk/sed的性能问题
  2. Android OpenCV Manager简介
  3. 复习Java字节流_字符流使用及案例
  4. 大数据处理与分析方向主要干什么_不了解干法制砂?6个影响干法制砂效果的主要因素及干法制砂生产加工7大技术要点分析...
  5. Linux 命令(49)—— export 命令(builtin)
  6. cart算法_ID3、C4.5、CART决策树算法
  7. 怎么用计算机知道密码,如何用电脑看到自家路由器的密码
  8. 模块“*.dll“已加载,但对DllRegisterServer的调用失败,错误代码0x8002801c
  9. java成语填充,java - 什么是“执行”这个成语?
  10. xslx-style导出,表头样式表格样式,指定条件
  11. 小程序审核出现的虚拟支付审核失败
  12. TLP(Transmission Line Pulse)
  13. 【pandas】教程:1-处理什么样的数据
  14. Linux下黑白棋c语言程序,黑白棋(C语言源程序)
  15. AI和区块链的融合会带来什么?
  16. python2和python3可以兼容吗_Python2和Python2和3兼容的方法,用于隐藏
  17. 电脑维修工具 硬盘检测工具 查查是否有坏道
  18. 写System权限的APP
  19. 合并报表怎么做快速简单?
  20. JavaWeb_01_HTMLCSS

热门文章

  1. 波士顿动力副总给我写了一封信,在我莫名膨胀之后
  2. 隔空操控iPhone!苹果新获悬停手势专利
  3. CycleGAN作者朱俊彦宣布重返CMU,担任助理教授
  4. Android HTTP2 + Oauth2 + Jwt 接口认证实例
  5. TypeScript基础入门 - 接口 - 继承接口
  6. 这可能是第二好的自定义 View 教程之属性动画
  7. Java8 lambda表达式10个示例
  8. Mysql分析性能(存储过程)
  9. centos7安装Samba服务
  10. JS中URL编码参数(UrlEncode)