Android 常用的分层架构
Android 中加载 UI 数据不是一件轻松的事,开发者经常需要处理各种边界情况。如各种生命周期和因为「配置更改」导致的 Activity 的销毁与重建。
「配置更改」的场景有很多:屏幕旋转,切换至多窗口模式,调整窗口大小,浅色模式与暗黑模式的切换,更改默认语言,更改字体大小等等
因此普遍处理方式是使用分层的架构。这样开发者就可以编写独立于 UI 的代码,而无需过多考虑生命周期,配置更改等场景。例如,我们可以在表现层(Presentation Layer
)的基础上添加一个领域层(Domain Layer
) 来保存业务逻辑,使用数据层(Data Layer
)对上层屏蔽数据来源(数据可能来自远程服务,可能是本地数据库)。
![](https://img-blog.csdnimg.cn/img_convert/df851244567a9eb4ac594ec3ff7d0a95.png)
表现层可以分成具有不同职责的组件:
View:处理生命周期回调,用户事件和页面跳转,Android 中主要是 Activity 和 Fragment
Presenter 或 ViewModel:向 View 提供数据,并不了解 View 所处的生命周期,通常生命周期比 View 长
Presenter 和 ViewModel 向 View 提供数据的机制是不同的,简单来说:
Presenter 通过持有 View 的引用并直接调用操作 View,以此向 View 提供数据
ViewModel 通过将可观察的数据暴露给观察者来向 View 提供数据
官方提供的可观察的数据
组件是 LiveData
。Kotlin 1.4.0 正式版发布之后,开发者有了新的选择:StateFlow
和 SharedFlow
。
最近网上流传出「LiveData 被弃用,应该使用 Flow 替代 LiveData」的声音。
LiveData
真的有那么不堪吗?Flow
真的适合你使用吗?
不人云亦云,只求接近真相。我们今天来讨论一下这两种组件。
ViewModel + LiveData
为了实现高效地加载 UI 数据,获得最佳的用户体验,应实现以下目标:
目标 1:已经加载的数据无需在「配置更改」的场景下再次加载
目标 2:避免在非活跃状态(不是
STARTED
或RESUMED
)下加载数据和刷新 UI目标 3:「配置更改」时不会中断的工作
Google 官方在 2017 年发布了架构组件库:使用 ViewModel
+ LiveData
帮助开发者实现上述目标。
![](https://img-blog.csdnimg.cn/img_convert/a700683f1f2901cea4d02db4fa77ffc6.png)
相信很多人在官方文档中见过这个图,ViewModel
比 Activity/Fragment
的生命周期更长,不受「配置更改」导致 Activity/Fragment
重建的影响。刚好满足了目标 1 和目标 3。
LiveData
是可生命周期感知的。新值仅在生命周期处于 STARTED
或 RESUMED
状态时才会分配给观察者,并且观察者会自动取消注册,避免了内存泄漏。LiveData
对实现目标 1 和 目标 2 很有用:它缓存其持有的数据的最新值,并将该值自动分派给新的观察者。
LiveData 的特性
既然有声音说「LiveData
要被弃用了」,那么我们先对 LiveData
进行一个全面的了解。聊聊它能做什么,不能做什么,以及使用过程中有哪些要注意的地方。
LiveData
是 Android Jetpack Lifecycle 组件中的内容。属于官方库的一部分,Kotlin/Java 均可使用。
一句话概括 LiveData
:LiveData 是可感知生命周期的,可观察的,数据持有者。
它的能力和作用很简单:更新 UI。
它有一些可以被认为是优点的特性:
观察者的回调永远发生在主线程
仅持有单个且最新的数据
自动取消订阅
提供「可读可写」和「仅可读」两个版本收缩权限
配合
DataBinding
实现「双向绑定」
观察者的回调永远发生在主线程
这个很好理解,LiveData
被用来更新 UI,因此 Observer
的 onChanged()
方法在主线程回调。
![](https://img-blog.csdnimg.cn/img_convert/64764b5c26a0ff3276dca4288a6583f0.png)
背后的原理也很简单,LiveData
的 setValue()
发生在主线程(非主线程调用会抛异常,postValue()
内部会切换到主线程调用 setValue()
)。之后遍历所有观察者的 onChanged()
方法。
仅持有单个且最新的数据
作为数据持有者(data holder),LiveData
仅持有 单个 且 最新 的数据。
单个且最新,意味着 LiveData
每次持有一个数据,并且新数据会覆盖上一个。
这个设计很好理解,数据决定了 UI 的展示,绘制 UI 时肯定要使用最新的数据,「过时的数据」应该被忽略。
配合
Lifecycle
,观察者只会在活跃状态下(STARTED
到RESUMED
)接收到LiveData
持有的最新的数据。在非活跃状态下绘制 UI 没有意义,是一种资源的浪费。
自动取消订阅
这是 LiveData
可感知生命周期的重要表现,自动取消订阅意味着开发者无需手动写那些取消订阅的模板代码,降低了内存泄漏的可能性。
背后原理是在生命周期处于 DESTROYED
时,移除观察者。
![](https://img-blog.csdnimg.cn/img_convert/699f0f8130692d94ce2413dec25d582b.png)
提供「可读可写」和「仅可读」两个版本
![](https://img-blog.csdnimg.cn/img_convert/77d124c0b948e3309a583d0b45314d54.png)
抽象类
LiveData
的setValue()
和postValue()
是 protected,而其实现类MutableLiveData
均为 public。
LiveData
提供了 mutable(MutableLiveData