MVVM系列之二:LiveData,2024金九银十

gradle依赖在上一篇中已经介绍了。下面来看基本用法:

  1. 创建LiveData实例,指定源数据类型

  2. 创建Observer实例,实现onChanged()方法,用于接收源数据变化并刷新UI

  3. LiveData实例使用observe()方法添加观察者,并传入LifecycleOwner

  4. LiveData实例使用setValue()/postValue()更新源数据 (子线程要postValue())

举个例子:

public class LiveDataTestActivity extends AppCompatActivity{

private MutableLiveData mLiveData;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_lifecycle_test);

//liveData基本使用

mLiveData = new MutableLiveData<>();

mLiveData.observe(this, new Observer() {

@Override

public void onChanged(String s) {

Log.i(TAG, "onChanged: "+s);

}

});

Log.i(TAG, "onCreate: ");

mLiveData.setValue(“onCreate”);//activity是非活跃状态,不会回调onChanged。变为活跃时,value被onStart中的value覆盖

}

@Override

protected void onStart() {

super.onStart();

Log.i(TAG, "onStart: ");

mLiveData.setValue(“onStart”);//活跃状态,会回调onChanged。并且value会覆盖onCreate、onStop中设置的value

}

@Override

protected void onResume() {

super.onResume();

Log.i(TAG, "onResume: ");

mLiveData.setValue(“onResume”);//活跃状态,回调onChanged

}

@Override

protected void onPause() {

super.onPause();

Log.i(TAG, "onPause: ");

mLiveData.setValue(“onPause”);//活跃状态,回调onChanged

}

@Override

protected void onStop() {

super.onStop();

Log.i(TAG, "onStop: ");

mLiveData.setValue(“onStop”);//非活跃状态,不会回调onChanged。后面变为活跃时,value被onStart中的value覆盖

}

@Override

protected void onDestroy() {

super.onDestroy();

Log.i(TAG, "onDestroy: ");

mLiveData.setValue(“onDestroy”);//非活跃状态,且此时Observer已被移除,不会回调onChanged

}

}

注意到 LiveData实例mLiveData的创建是使用MutableLiveData,它是LiveData的实现类,且指定了源数据的类型为String。然后创建了接口Observer的实例,实现其onChanged()方法,用于接收源数据的变化。observer和Activity一起作为参数调用mLiveData的observe()方法,表示observer开始观察mLiveData。然后Activity的所有生命周期方法中都调用了mLiveData的setValue()方法。 结果日志打印如下:

//打开页面,

2020-11-22 20:23:29.865 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onCreate:

2020-11-22 20:23:29.867 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStart:

2020-11-22 20:23:29.868 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onStart

2020-11-22 20:23:29.869 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onResume:

2020-11-22 20:23:29.869 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onResume

//按Home键

2020-11-22 20:23:34.349 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onPause:

2020-11-22 20:23:34.349 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onPause

2020-11-22 20:23:34.368 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStop:

//再点开

2020-11-22 20:23:39.145 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStart:

2020-11-22 20:23:39.146 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onStart

2020-11-22 20:23:39.147 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onResume:

2020-11-22 20:23:39.147 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onResume

//返回键退出

2020-11-22 20:23:56.753 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onPause:

2020-11-22 21:23:56.753 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onPause

2020-11-22 20:23:58.320 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onStop:

2020-11-22 20:23:58.322 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onDestroy:

  • 首先打开页面,onCreate()中setValue,由于activity是非活跃状态,不会立即回调onChanged。当走到onStart()变为活跃时,onChanged被调用,但value被onStart()中setValue的value覆盖,所以打印的是onChanged: onStart。(为啥不是连续打印两次呢?,是因为ON_START事件是在onStart() return之后,即onStart()走完之后才变为活跃<详见上一篇>,此时observer接收最新的数据。) 接着走到onResume(),也setValue了,同样是活跃状态,所以立刻回调onChanged,打印onChanged: onResume

  • 按Home键时,onPause()中setValue,活跃状态,立刻回调onChanged方法。onStop()执行时已经变为非活跃状态,此时setValue不会立即回调onChanged方法。

  • 再点开时,走到onStart()变为活跃时,onChanged被调用,但value被onStart()中setValue的value覆盖,所以打印的是onChanged: onStart。接着走到onResume(),也setValue了,同样是活跃状态,所以立刻回调onChanged。

  • 返回键退出时,onPause()/onStop()的效果和按Home键一样。onDestroy()中setValue,此时非活跃状态,且此时observer已被移除,不会回调onChanged。

另外,除了使用observe()方法添加观察者,也可以使用observeForever(Observer) 方法来注册未关联 LifecycleOwner的观察者。在这种情况下,观察者会被视为始终处于活跃状态。

2.2 扩展使用


扩展包括两点:

  1. 自定义LiveData,本身回调方法的覆写:onActive()、onInactive()。

  2. 实现LiveData为单例模式,便于在多个Activity、Fragment之间共享数据。

官方的例子如下:

public class StockLiveData extends LiveData {

private static StockLiveData sInstance; //单实例

private StockManager stockManager;

private SimplePriceListener listener = new SimplePriceListener() {

@Override

public void onPriceChanged(BigDecimal price) {

setValue(price);//监听到股价变化 使用setValue(price) 告知所有活跃观察者

}

};

//获取单例

@MainThread

public static StockLiveData get(String symbol) {

if (sInstance == null) {

sInstance = new StockLiveData(symbol);

}

return sInstance;

}

private StockLiveData(String symbol) {

stockManager = new StockManager(symbol);

}

//活跃的观察者(LifecycleOwner)数量从 0 变为 1 时调用

@Override

protected void onActive() {

stockManager.requestPriceUpdates(listener);//开始观察股价更新

}

//活跃的观察者(LifecycleOwner)数量从 1 变为 0 时调用。这不代表没有观察者了,可能是全都不活跃了。可以使用hasObservers()检查是否有观察者。

@Override

protected void onInactive() {

stockManager.removeUpdates(listener);//移除股价更新的观察

}

}

为了观察股票价格变动,继承LiveData自定义了StockLiveData,且为单例模式,只能通过get(String symbol)方法获取实例。 并且重写了onActive()、onInactive(),并加入了 开始观察股价更新、移除股价更新观察 的逻辑。

  • onActive()调用时机为:活跃的观察者(LifecycleOwner)数量从 0 变为 1 时。

  • onInactive()调用时机为:活跃的观察者(LifecycleOwner)数量从 1 变为 0 时。

也就是说,只有当 存在活跃的观察者(LifecycleOwner)时 才会连接到 股价更新服务 监听股价变化。使用如下:

public class MyFragment extends Fragment {

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

//获取StockLiveData单实例,添加观察者,更新UI

StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {

// Update the UI.

});

}

}

由于StockLiveData是单实例模式,那么多个LifycycleOwner(Activity、Fragment)间就可以共享数据了。

2.3 高级用法


如果希望在将 LiveData 对象分派给观察者之前对存储在其中的值进行更改,或者需要根据另一个实例的值返回不同的 LiveData 实例,可以使用LiveData中提供的Transformations类。

2.3.1 数据修改 - Transformations.map

//Integer类型的liveData1

MutableLiveData liveData1 = new MutableLiveData<>();

//转换成String类型的liveDataMap

LiveData liveDataMap = Transformations.map(liveData1, new Function<Integer, String>() {

@Override

public String apply(Integer input) {

String s = input + " + Transformations.map";

Log.i(TAG, "apply: " + s);

return s;

}

});

liveDataMap.observe(this, new Observer() {

@Override

public void onChanged(String s) {

Log.i(TAG, "onChanged1: "+s);

}

});

liveData1.setValue(100);

使用很简单:原本的liveData1 没有添加观察者,而是使用Transformations.map()方法 对liveData1的数据进行的修改 生成了新的liveDataMap,liveDataMap添加观察者,最后liveData1设置数据 。

此例子把 Integer类型的liveData1 修改为String类型的liveDataMap。结果如下:

2020-12-06 17:01:56.095 21998-21998/com.hfy.androidlearning I/Lifecycle_Test: apply: 100 + Transformations.map

2020-12-06 17:01:56.095 21998-21998/com.hfy.androidlearning I/Lifecycle_Test: onChanged1: 100 + Transformations.map

2.3.2 数据切换 - Transformations.switchMap

如果想要根据某个值 切换观察不同LiveData数据,则可以使用Transformations.switchMap()方法。

//两个liveData,由liveDataSwitch决定 返回哪个livaData数据

MutableLiveData liveData3 = new MutableLiveData<>();

MutableLiveData liveData4 = new MutableLiveData<>();

//切换条件LiveData,liveDataSwitch的value 是切换条件

MutableLiveData liveDataSwitch = new MutableLiveData<>();

//liveDataSwitchMap由switchMap()方法生成,用于添加观察者

LiveData liveDataSwitchMap = Transformations.switchMap(liveDataSwitch, new Function<Boolean, LiveData>() {

@Override

public LiveData apply(Boolean input) {

//这里是具体切换逻辑:根据liveDataSwitch的value返回哪个liveData

if (input) {

return liveData3;

}

return liveData4;

}

});

liveDataSwitchMap.observe(this, new Observer() {

@Override

public void onChanged(String s) {

Log.i(TAG, "onChanged2: " + s);

}

});

boolean switchValue = true;

liveDataSwitch.setValue(switchValue);//设置切换条件值

liveData3.setValue(“liveData3”);

liveData4.setValue(“liveData4”);

liveData3、liveData4是两个数据源,有一个判断条件来决定 取哪一个数据 ,这个条件就是liveDataSwitch,如果值为true则取liveData3,false则取liveData4。 Transformations.switchMap()就用于实现这一逻辑,返回值liveDataSwitchMap添加观察者就可以了。 结果如下:

2020-12-06 17:33:53.844 27347-27347/com.hfy.androidlearning I/Lifecycle_Test: switchValue=true

2020-12-06 17:33:53.847 27347-27347/com.hfy.androidlearning I/Lifecycle_Test: onChanged2: liveData3

2020-12-06 17:34:37.600 27628-27628/com.hfy.androidlearning I/Lifecycle_Test: switchValue=false

2020-12-06 17:34:37.602 27628-27628/com.hfy.androidlearning I/Lifecycle_Test: onChanged2: liveData4

(Transformations对LivaData这两个用法和Rxjava简直一毛一样)

2.3.3 观察多个数据 - MediatorLiveData

MediatorLiveData 是 LiveData 的子类,允许合并多个 LiveData 源。只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。

MediatorLiveData mediatorLiveData = new MediatorLiveData<>();

MutableLiveData liveData5 = new MutableLiveData<>();

MutableLiveData liveData6 = new MutableLiveData<>();

//添加 源 LiveData

mediatorLiveData.addSource(liveData5, new Observer() {

@Override

public void onChanged(String s) {

Log.i(TAG, "onChanged3: " + s);

mediatorLiveData.setValue(s);

}

});

//添加 源 LiveData

mediatorLiveData.addSource(liveData6, new Observer() {

@Override

public void onChanged(String s) {

Log.i(TAG, "onChanged4: " + s);

mediatorLiveData.setValue(s);

}

});

//添加观察

mediatorLiveData.observe(this, new Observer() {

@Override

public void onChanged(String s) {

Log.i(TAG, "onChanged5: "+s);

//无论liveData5、liveData6更新,都可以接收到

}

});

liveData5.setValue(“liveData5”);

//liveData6.setValue(“liveData6”);

例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向 MediatorLiveData 对象添加以下源:

  • 与存储在本地数据库中的数据关联的 liveData5

  • 与从网络访问的数据关联的 liveData6

Activity 只需观察 MediatorLiveData 对象即可从这两个源接收更新。 结果如下:

2020-12-06 17:56:17.870 29226-29226/com.hfy.androidlearning I/Lifecycle_Test: onChanged3: liveData5

2020-12-06 17:56:17.870 29226-29226/com.hfy.androidlearning I/Lifecycle_Test: onChanged5: liveData5

(Transformations也是对MediatorLiveData的使用。)

LiveData的使用就讲完了,下面开始源码分析。

3、源码分析

======

前面提到 LiveData几个特点,能感知生命周期状态变化、不用手动解除观察等等,这些是如何做到的呢?

3.1 添加观察者


LiveData原理是观察者模式,下面就先从LiveData.observe()方法看起:

/**

  • 添加观察者. 事件在主线程分发. 如果LiveData已经有数据,将直接分发给observer。

  • 观察者只在LifecycleOwner活跃时接受事件,如果变为DESTROYED状态,observer自动移除。

  • 当数据在非活跃时更新,observer不会接收到。变为活跃时 将自动接收前面最新的数据。

  • LifecycleOwner非DESTROYED状态时,LiveData持有observer和 owner的强引用,DESTROYED状态时自动移除引用。

  • @param owner 控制observer的LifecycleOwner

  • @param observer 接收事件的observer

*/

@MainThread

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {

assertMainThread(“observe”);

if (owner.getLifecycle().getCurrentState() == DESTROYED) {

// LifecycleOwner是DESTROYED状态,直接忽略

return;

}

//使用LifecycleOwner、observer 组装成LifecycleBoundObserver,添加到mObservers中

LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长。而不成体系的学习效果低效漫长且无助。时间久了,付出巨大的时间成本和努力,没有看到应有的效果,会气馁是再正常不过的。

所以学习一定要找到最适合自己的方式,有一个思路方法,不然不止浪费时间,更可能把未来发展都一起耽误了。

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。

2691)]
[外链图片转存中…(img-uuVDrBJ1-1711692052692)]
[外链图片转存中…(img-NAo81BrX-1711692052692)]
[外链图片转存中…(img-jFVQmxph-1711692052693)]
[外链图片转存中…(img-aydrt1gL-1711692052693)]
[外链图片转存中…(img-D5e10t3L-1711692052694)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-NcsDAFrI-1711692052694)]

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长。而不成体系的学习效果低效漫长且无助。时间久了,付出巨大的时间成本和努力,没有看到应有的效果,会气馁是再正常不过的。

所以学习一定要找到最适合自己的方式,有一个思路方法,不然不止浪费时间,更可能把未来发展都一起耽误了。

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值