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的更多相关文章

  1. Android官方架构组件介绍之LiveData(二)

    LiveData LiveData是一个用于持有数据并支持数据可被监听(观察).和传统的观察者模式中的被观察者不一样,LiveData是一个生命周期感知组件,因此观察者可以指定某一个LifeCycle ...

  2. Android官方架构组件介绍之LifeCycle(一)

    Android官方架构组件介绍之LifeCycle 下面是官方提供的Android App开发的架构图: 从上图可以看到一些关键字:ViewModel,LiveData,Room等.其实看了上面视频的 ...

  3. Android官方架构组件介绍之LifeCycle

    Google 2017 I/O开发者大会于近日召开,在开发者大会上谷歌除了发布了Android O等一些新产品之外,也对Android代码的架构做出了一个官方的回应. Google 2017 I/O开 ...

  4. Android官方架构组件介绍之ViewModel

    ViewModel 像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的.Framework可能会根据用户的一些操作和设备的状态对Act ...

  5. Android官方架构组件介绍之ViewModel(三)

    ViewModel 像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的.Framework可能会根据用户的一些操作和设备的状态对Act ...

  6. Android官方架构组件介绍之应用(四)

    讲一个项目常见的功能,友盟统计功能 例如一个项目有很多多modlue,每个里面modlue都有Activity,Activity需要友盟统一,Fragment也需要友盟统计.一般做法就是继承一个Bas ...

  7. 改造 Android 官方架构组件 ViewModel

    前言 Android 官方架构组件在今年 5 月份 Google I/O 大会上被公布, 直到 11 月份一直都是测试版, 由于工作比较繁忙, 期间我只是看过类似的文章, 但没有在实际项目中使用过, ...

  8. Android官方架构组件指南

    此指南适用于那些曾经或现在进行Android应用的基础开发,并希望了解和学习编写Android程序的最佳实践和架构.通过学习来构建强大的生产级别的应用. 注意:此指南默认你对Android开发有比较深 ...

  9. Android系统架构说明介绍

    Android系统架构说明介绍 Android系统架构和一些普遍的操作系统差不多,都是采用了分层的架构,从他们之间的架构图看,Android系统架构分为四个层,从高层到低层分别是应用程序层.应用程序框 ...

随机推荐

  1. MySQL 常用语法 之 DISTINCT

    DISTINCT作用很简单就是去除重复行的数据. 具体看下面列子 表A数据[两条 nami 99] nameA   scoreA robin    98 nami    99 saber  98 lu ...

  2. GIt的基本知识

    以前已经把git 看过一遍了,由于好久没有用它 ,现在已经忘了.现在呢,要用它进行同步代码,所以呢,我打算记一记,再复习复习. 参考文件:https://git-scm.com/book/zh/v2 ...

  3. C# 发邮件 服务器响应为: 5.7.0 Must issue a STARTTLS command first

    The SMTP server requires a secure connection or the client was not authenticated. The server respons ...

  4. (转)RTP协议全解(H264码流和PS流)

    写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢. 互联网的发展离不开大家的无私奉献,我决定从我做起,希 ...

  5. caffe源代码分析--Blob类代码研究

    作者:linger 转自须注明转自:http://blog.csdn.net/lingerlanlan/article/details/24379689 数据成员 shared_ptr<Sync ...

  6. Fastqc 能够识别的碱基编码格式

    Fastqc 能够自动识别序列的碱基编码格式,我查看一下源代码,发现是碱基编码格式一共分为 1)sanger/illumina 1.9 2) illumina 1.3 3) illumina 1.5 ...

  7. BWT转换对字符串进行编码

    今天看了下bowtie 的论文, 里面描述了BWT转换的过程和bowtie的比对算法: NGS测序数据的数据量非常大, 为了更快的处理, 通常需要对数据进行压缩:而BWT实际上就是一种数据转换方法, ...

  8. 最短路径问题-Floyd算法

    概念 最短路径也是图的一个应用,即寻找图中某两个顶点的最短路径长度. 实际应用:例如确定某两个城市间的坐火车最短行车路线长度等. Floyd algorithm 中文名就是弗洛伊德算法. 算法思路:用 ...

  9. Struts2 ajax json使用介绍

    一.jar包首先引入Struts和json所需的jar包. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRteWhvbWUxOTkw/font/5a6 ...

  10. Solr with Apache Tomcat

    配置教程 1 http://www.duntuk.com/how-install-apache-solr-46-apache-tomcat-7-use-drupal https://www.gotos ...