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变的更便利,能给我们省去不少逻辑代码。所以它还是很巧妙的一个控件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容