热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android官方架构组件介绍之LiveData

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

LiveData

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

如果观察者指定LifeCycle处于Started或者RESUMED状态,LiveData会将观察者视为活动状态,并通知其数据的变化。

我们看一段代码:

public class LocationLiveData extends LiveData {
    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 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 {
    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 userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

这里我们如果只需要知道变化用户的名字,那么只要观察userName这个LiveData对象即可。它会从userLiveData数据中提取用户名并传递给它自己的观察者。

  • Transformations.switchMap()

与Transformations.map()类似,只不过这里传递个switchMap()的方法必须返回一个LiveData对象。

private LiveData getUser(String id) {
  ...;
}

LiveData userId = ...;
LiveData 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 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 addressInput = new MutableLiveData();
    public final LiveData 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 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • JVM钩子函数的应用场景详解
    本文详细介绍了JVM钩子函数的多种应用场景,包括正常关闭、异常关闭和强制关闭。通过具体示例和代码演示,帮助读者更好地理解和应用这一机制。适合对Java编程和JVM有一定基础的开发者阅读。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 本文将带你快速了解 SpringMVC 框架的基本使用方法,通过实现一个简单的 Controller 并在浏览器中访问,展示 SpringMVC 的强大与简便。 ... [详细]
  • com.sun.javadoc.PackageDoc.exceptions()方法的使用及代码示例 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
author-avatar
陈浩颖美娇承湖_527
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有