Android jetpack - LiveData 可观察数据存储类

一、前言

LiveData 可观察数据存储类属于谷歌在2018推出Android jetpack(外网)其中的软件架构组件中的一个。在谷歌开发者网站有详细介绍LiveData(外网)。本文以总结为目的,采用更加清晰和有条理的方式解读LiveData。
阅读前准备
便于理解,如果你已经知道什么是Lifecycle观察者模式ViewModel 更好,那么理解此篇文章便轻车熟路。
避免过多而杂的介绍,部分知识不详述,请阅读笔者链接文章或者Google。

全文就一个核心
LiveData是结合Lifecycle和观察者模式的数据存储类

二、LiveData

谷歌爸爸的介绍
LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

简单理解
LiveData就是结合生命周期+观察者模式的数据存储类。方便我们根据生命周期来监听数据变化的。由于这个结构的特性,所以它拥有以下特点
1、确保界面符合数据状态;2、不会发生内存泄露;3、不会因 Activity 停止而导致崩溃;4、不再需要手动处理生命周期;5、数据始终保持最新状态;6、适当的配置更改;7、共享资源

导入

implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"

三、详细介绍

LiveData介绍就如下几个点

  • LiveData基本使用
  • LiveData自定义LiveData
  • LiveDa数据转换
  • LiveData合并多个数据

3.1、基本使用

这里说下为啥别的文章或者官网都是结合ViewModel来讲的例子,ViewModel就是存储和管理界面数据的一个类。我们LiveData就是一种数据。如果不和ViewModel一起讲,不影响我们理解LiveData的核心思想。但是并不推荐直接在Activity或者Fragment里直接写LiveData变量来存储数据。这会让他们过于臃肿,所以还是用推荐存储数据的方式ViewModel。
如果你还不了解ViewModel你可以简单理解为它就是存储数据的一个地方。

LiveData在使用上除了和ViewModel结合以外无非观察者模式三个步骤

  • 创建LiveData,即创建被观察者
  • 添加Observe,添加观察者
  • 数据改变是修改UI,观察者监听
public class MyViewModel extends ViewModel {
    MutableLiveData<String> name;

    public MutableLiveData<String> getName() {
        if (name == null) {
            name = new MutableLiveData<>();
        }
        return name;
    }
}

    ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication());
    ViewModelProvider viewModelProvider = new ViewModelProvider(this, factory);
    viewModel = viewModelProvider.get(MyViewModel.class);
    viewModel.getName().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            textView.setText(s);
        }
    });

  viewModel.getName().setValue("yink");

1、MyViewModel就是我们创建LiveData的地方。
2、viewModel.getName()拿到LiveData对象,observer添加观察者,this是Lifecycle生命周期组件。
3、当数据变化setValue时,会自动调用观察者的onChanged方法,我们就可以更新UI或其它操作了。

  • 其中在理解ViewModel上可不必太追究细节,理解为创建对象即可
  • 至于observe的参数this参数,带入的是Lifecycle,不太理解生命周期请移步Lifecycle
  • 为啥用MutableLiveData对象是因为MutableLiveData提供set,get方法。而LiveData只对外开放get方法,LiveData的set,get代码如下:
protected void setValue(T value) {

public T getValue() {

3.2、自定义LiveData

public class MyLiveData<T> extends MutableLiveData<T> {
    @Override
    protected void onActive() {
        super.onActive();
        Log.d("yink","onActive");
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.d("yink","onInactive");
    }
}

自定义LiveData的时候,跟我们其它继承用法一样。说下onActive和onInactive什么时候调用

  • onActive 两个条件:添加了observe + 生命周期alive。
  • onInactive 移除了observe,或者生命周期非alive。
    例:点击home到后台:onInactive; 重新进入页面:onActive ; 移除observe:onInactive

3.3、转换LiveData

Livecycle给我提供了两种方法来转换LiveData
Transformations.map:把LiveData的值改变一下,目的是转换值
Transformations.switchMap:每次改变值时提供一个全新的liveData对象并监听这个新的liveData对象
下面我用相同的例子来讲解一下他们的区别,便于大家理解
我有一个MutableLiveData<Integer> adressNumber;来表示区号,然后我们要转换这个区号为当前哪个市区。于是有下面的代码

MutableLiveData<Integer> adressNumber;
    MutableLiveData<String> adressStringOne = (MutableLiveData<String>) Transformations.map(adressNumber, new Function<Integer, String>() {
        @Override
        public String apply(Integer input) {
            return getAdressString(input);
        }
    });

    MutableLiveData<String> adressStringTwo = (MutableLiveData<String>) Transformations.switchMap(adressNumber, new Function<Integer, LiveData<String>>() {
        @Override
        public LiveData<String> apply(Integer input) {
            MutableLiveData mutableLiveData = new MutableLiveData<String>();
            mutableLiveData.setValue(getAdressString(input));
            return mutableLiveData;
        }
    });

    private String getAdressString(Integer number) {
        if (number == 0755) return "深圳";
        return "其它";
    }

例子很简单,就是输入区号0755,查询出来是深圳。
map用法:
1、adressStringOne这个LiveData的值就是一个String,通过输入的0755来转换成”深圳“
2、adressStringOne对象没有变,每次监听adressNumber来设置自己的值
switchMap用法:
1、adressStringTwo这个LiveData同样拥有一个String值,通过输入的0755来转换成”深圳“
2、adressStringTwo和map用法不同的是,他拿到”深圳”这个值后,不是直接设置,而是new一个新的LiveData<String>,adressStringTwo来监听LiveData<String>变化并且和新的LiveData<String>的值保持同步。
原理
原理也比较简单,贴出源码结合我上面的例子就很好理解了。

public interface Function<I, O> {
    O apply(I input);
}

@MainThread
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

1、Function.apply只是方便我们自己去转换值
2、MediatorLiveData对象继承自MutableLiveData它提供的addSource方法可以观察LiveData对象,这也就是map和switchMap的关键实现。让LiveData可以观察LiveData
3、传入source,被观察者,它一变化就调用onChanged方法。在onChanged里MediatorLiveData设置新的值
4、流程简单总结就是:对象A来观察B的值,根据B的变化,A设置一个新的值,A观察B

@MainThread
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }

1、Function.applyMediatorLiveData和上面map一个用法
2、当第一次x变化,进入onChanged,mSource = newLiveData,MediatorLiveData就会根据新的newLiveData来监听它的变化,addSource持值同步。
3、当第二次x变化,进入onChanged,mSource = newLiveData根据java局部变量特性,newLiveData是不同于第一次x变化时的newLiveData对象。此时会result.removeSource(mSource);移除之前的监听,再重新监听这个新newLiveData的值,并且addSource保持值同步。
4、switchMapFunction的代码从头到尾,result.addSource了两次,也就是有两个监听
5、流程简单总结就是:A观察B的值,B变化必然创建一个新的C,A观察C。所以A同时观察B和C,只不过这个B每次变化会导致A监听一个新的C
6、当然如果你写switchMapFunction.apply(x);实现的时候,返回的是同一个对象,那么C也不会变。

3.4、合并多个LiveData

合并多个LiveData目的很明确,比如一个按钮需要两个LiveData状态来确定, 你就需要同时监听两个LiveData才行。直接抛出一个例子,很简单就不详解了。

        mediatorLiveData.addSource(addressNumber, new Observer() {
            @Override
            public void onChanged(Object o) {
                Address address = (Address)mediatorLiveData.getValue();
                address.setNum((Integer)o);
                mediatorLiveData.setValue(address);
            }
        });
        mediatorLiveData.addSource(addressString, new Observer() {
            @Override
            public void onChanged(Object o) {
                Address address = (Address)mediatorLiveData.getValue();
                address.setString((String) o);
                mediatorLiveData.setValue(address);
            }
        });
        mediatorLiveData.observe(this, new Observer() {
            @Override
            public void onChanged(Object o) {
                
            }
        });

class Address {
        Integer num;
        String string;
        ...
}

四、写在最后

整体来看LiveData还是一个很简单的控件,它的核心思想 = Lifecycle + 观察者模式。优点离不开它的核心思想,简单来说生命周期有效性不用我们过多担心了,还有就是观察者模式的优点让我们刷新UI变的更便利,能给我们省去不少逻辑代码。所以它还是很巧妙的一个控件。

推荐阅读更多精彩内容