Android官方架构组件介绍之LiveData
LiveData
LiveData是一个用于持有数据并支持数据可被监听(观察)。和传统的观察者模式中的被观察者不一样,LiveData是一个生命周期感知组件,因此观察者可以指定某一个LifeCycle给LiveData,并对数据进行监听。
如果观察者指定LifeCycle处于Started或者RESUMED状态,LiveData会将观察者视为活动状态,并通知其数据的变化。
我们看一段代码:
public class LocationLiveData extends LiveData<Location> {
private LocationManager locationManager;
private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};
public LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}
上面有三个值得注意的地方:
- onActive()
当这个方法被调用时,表示LiveData的观察者数量从0变为了1,这时就我们的位置监听来说,就应该注册我们的时间监听了。
- onInactive()
这个方法被调用时,表示LiveData的观察者数量变为了0,既然没有了观察者,也就没有理由再做监听,此时我们就应该将位置监听移除。
- setValue()
通过调用这个方法来更新LiveData的数据,并通知处于活动状态的观察者。
接着我们就能像下面这样使用LocationLiveData了。
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
LiveData<Location> myLocationListener = ...;
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.addObserver(this, location -> {
// update UI
});
}
});
}
}
注意上面的addObserver方法,我们将LifeCycleOwner作为第一个参数传递了进去,这表示我们的LocationLiveData将遵照这个Fragment所持有的LifeCycle办事。
- 如果LifeCycle不在Started或者RESUMED这两个状态,那么观察者将无法接受到数据更新的回调,即使数据发生了变化。
- 如果LifeCycle销毁了,即生命周期结束,观察者将被自动从LiveData中移除。
既然LocationLiveData是生命周期感知的,那么我们就可以稍微改动一下它的代码,让它可以被多个Activity或者Fragment公用:
public class LocationLiveData extends LiveData<Location> {
private static LocationLiveData sInstance;
private LocationManager locationManager;
@MainThread
public static LocationLiveData get(Context context) {
if (sInstance == null) {
sInstance = new LocationLiveData(context.getApplicationContext());
}
return sInstance;
}
private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};
private LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}
这里使用单例的原因就是让多个Activity或者Fragment共享一个LocationLiveData实例。
然后我们可以这么使用:
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
Util.checkUserStatus(result -> {
if (result) {
MyLocationListener.get(getActivity()).addObserver(this, location -> {
// update UI
});
}
});
}
}
通过这么一改,现在即使有多个Activity或者Fragment在使用LocationLiveData,它也能对其进行优雅的管理。不必理会页面销毁带来的诸多麻烦。
总结几点LiveData的有点:
- 没有内存溢出
当观察者被绑定他们对应的LifeCycle以后,当页面销毁时他们会自动被溢出,不会导致内存溢出。
- 不会因为Activity的不可见导致Crash
当Activity不可见时,即使有数据变化,LiveData也不会通知观察者。因为此时观察者的LifeCyele并不处于Started或者RESUMED状态。
- 配置的改变
当当前Activity配置改变(如屏幕方向),导致重新从onCreate走一遍,这是观察者们会立刻收到配置变化前的最新数据。
- 资源共享
我们只需要一个LocationLivaData,连接系统服务一次,就能支持所有的观察者。
- 不再有人为生命周期处理
通过上面的代码可以知道,我们的Activity或者Fragment只要在需要观察数据的时候观察数据即可,不需要理会生命周期变化了。这一切都交给LiveData来自动管理。
LiveData的转换
有时候有这样的需求,需要在LiveData将变化的数据通知给观察者前,改变数据的类型;或者是返回一个不一样的LiveData。
这里介绍一个类Transformations,它可以帮助完成上面的这些操作。
- Transformations.map()
在LiveData数据的改变传递到观察者之前,在数据上应用一个方法:
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
这里我们如果只需要知道变化用户的名字,那么只要观察userName这个LiveData对象即可。它会从userLiveData数据中提取用户名并传递给它自己的观察者。
- Transformations.switchMap()
与Transformations.map()类似,只不过这里传递个switchMap()的方法必须返回一个LiveData对象。
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
当你考虑在ViewModel中使用LifeCycle对象时,这种转换就是一个可选的解决方案。
假如有一下需求,用户输入一个地址,我们在屏幕上更新这个地址对应的邮编,简单的写法如下:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository;
}
private LiveData<String> getPostalCode(String address) {
// DON'T DO THIS
return repository.getPostCode(address);
}
}
这样写问题显然很严重,当每次调用getPostalCode方法后,UI代码中都需要对getPostalCode的返回值做注册观察者操作,并且还要移除上一个观察者,这样显然是低效率的。此外,如果这时UI因为配置的变化(屏幕旋转)重建了,那么它会触发再次调用getPostalCode,而不是使用之前的调用结果。
因此我们可以做如下转换:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveData<String> addressInput = new MutableLiveData();
public final LiveData<String> postalCode =
Transformations.switchMap(addressInput, (address) -> {
return repository.getPostCode(address);
});
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository
}
private void setInput(String address) {
addressInput.setValue(address);
}
}
注意,这里我们将postalCode访问限制符写成public final,因为它将始终不变,UI只要在需要用的时候将观察者注册到postalCode中就行。这是当用户调用setInput后,如果postalCode上有可活动的观察者,那么repository.getPostCode(address)就会被调用,如果此时没有可活动的观察者,则repository.getPostCode(address)不会被调用。
自定义转换
在你的应用中可能需要除了上面两种以外更多的LiveData的转换,为了实现这些转换,你可以使用MediatorLiveData类,它可以用来正确的处理其他多个LiveData的事件变化,并处理这些事件。MediatorLiveData会将自身的active/inactive状态变化正确的传递给它所处理的LiveData,例如MediatorLiveData没有观察者的话,
Android官方架构组件介绍之LiveData的更多相关文章
- Android官方架构组件介绍之LiveData(二)
LiveData LiveData是一个用于持有数据并支持数据可被监听(观察).和传统的观察者模式中的被观察者不一样,LiveData是一个生命周期感知组件,因此观察者可以指定某一个LifeCycle ...
- Android官方架构组件介绍之LifeCycle(一)
Android官方架构组件介绍之LifeCycle 下面是官方提供的Android App开发的架构图: 从上图可以看到一些关键字:ViewModel,LiveData,Room等.其实看了上面视频的 ...
- Android官方架构组件介绍之LifeCycle
Google 2017 I/O开发者大会于近日召开,在开发者大会上谷歌除了发布了Android O等一些新产品之外,也对Android代码的架构做出了一个官方的回应. Google 2017 I/O开 ...
- Android官方架构组件介绍之ViewModel
ViewModel 像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的.Framework可能会根据用户的一些操作和设备的状态对Act ...
- Android官方架构组件介绍之ViewModel(三)
ViewModel 像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的.Framework可能会根据用户的一些操作和设备的状态对Act ...
- Android官方架构组件介绍之应用(四)
讲一个项目常见的功能,友盟统计功能 例如一个项目有很多多modlue,每个里面modlue都有Activity,Activity需要友盟统一,Fragment也需要友盟统计.一般做法就是继承一个Bas ...
- 改造 Android 官方架构组件 ViewModel
前言 Android 官方架构组件在今年 5 月份 Google I/O 大会上被公布, 直到 11 月份一直都是测试版, 由于工作比较繁忙, 期间我只是看过类似的文章, 但没有在实际项目中使用过, ...
- Android官方架构组件指南
此指南适用于那些曾经或现在进行Android应用的基础开发,并希望了解和学习编写Android程序的最佳实践和架构.通过学习来构建强大的生产级别的应用. 注意:此指南默认你对Android开发有比较深 ...
- Android系统架构说明介绍
Android系统架构说明介绍 Android系统架构和一些普遍的操作系统差不多,都是采用了分层的架构,从他们之间的架构图看,Android系统架构分为四个层,从高层到低层分别是应用程序层.应用程序框 ...
随机推荐
- 写给大忙人的JavaSE 8 - 学习
前面有提到过lambda和函数式接口,但是JavaSE 8 除了这两个新特性之后还提供了很多有用的东西.例如Stream. 摸索了几天,终于弄明白Stream的应用了. 先推荐一篇文章:Java 8 ...
- C#的HttpWebRequest编程,支持带ViewState的网页POST请求
staticprivatestring SearchURL(string id) { try { //Get the ViewState and EventValidation HttpWebRequ ...
- (转)platform_driver_register,什么时候调用PROBE函数 注册后如何找到驱动匹配的设备
platform_driver_register,什么时候调用PROBE函数 注册后如何找到驱动匹配的设备 2011-10-24 19:47:07 分类: LINUX kernel_init中d ...
- XML 入门
XML语法 所有 XML 元素都须有关闭标签 XML 标签对大小写敏感 XML 必须正确地嵌套 XML 文档必须有根元素 就像HTML一样,HTML必须有<html>根元素.XML也必须有 ...
- 比較JS合并数组的各种方法及其优劣
原文链接: Combining JS Arrays原文日期: 2014-09-09翻译日期: 2014-09-18翻译人员: 铁锚 本文属于JavaScript的基础技能. 我们将学习结合/合并两个 ...
- sublime text 删除插件
1.ctrl+shift+p 输入remove package 选择要删掉的插件即可 2.去掉产生临死文件的插件:phptools
- VCL 中的 Windows API 函数(6): BeginDeferWindowPos
BeginDeferWindowPos 和 DeferWindowPos.EndDeferWindowPos 是一组一起使用的函数, 可对一组窗口的位置.大小.Z 序等进行调整, 在 ExtCtrls ...
- linux环境中查看主机型号(机器型号)
需求说明: 今天一同事让统计测试环境主机型号,在此记录下. 操作过程: 1.通过dmidecode工具查询,产品型号(机器型号) [root@redhat6 ~]# dmidecode | grep ...
- [java] java 线程join方法详解
join方法的作用是使所属线程对象正常执行run方法,而对当前线程无限期阻塞,直到所属线程销毁后再执行当前线程的逻辑. 一.先看普通的无join方法NoJoin.java public class N ...
- mac平台安装类似yum的工具
首先从Apple Store下载Xcode,然后安装Xcode,接着安装Homebrew包管理,类似于Ubuntu下的apt-get: 终端下输入ruby -e "$(curl -fsSL ...