LiveData 的缺陷:为什么我现在越来越少使用 LiveData?

我第一次接触 LiveData 的时候,觉得它非常惊艳。

在那个时代,Android 开发充满了 Callback、生命周期问题、内存泄漏风险。

LiveData 的出现,让 Android 开发第一次拥有了一个和生命周期结合的数据观察方案。

那时候我觉得:

LiveData 真的好棒,简单好用。

但是几年之后,我越来越少使用 LiveData。

现在的新项目里,我基本已经不会主动选择 LiveData,而更多使用 Kotlin Flow。

这是因为随着 Android 应用复杂度提升,LiveData 本身的一些设计限制越来越明显。


一、刚出道的 LiveData,为什么让我觉得惊艳?

在 LiveData 出现之前,Android 异步开发是什么样?

大量 Callback:

java 复制代码
api.getUser(new Callback<User>() {

    @Override
    public void onSuccess(User user) {

        textView.setText(user.name);

    }

});

看起来简单,但是隐藏大量问题。


1. 生命周期问题

比如:

复制代码
打开页面

↓

请求网络

↓

用户返回

↓

Activity销毁

↓

接口返回

↓

更新UI

↓

Crash

开发者需要自己判断:

java 复制代码
if(!activity.isDestroyed()){

}

大量生命周期判断代码。


2. 数据和 UI 强绑定

以前:

复制代码
请求成功

↓

Callback

↓

修改UI

业务代码和 UI 更新混在一起。


LiveData 出现:

kotlin 复制代码
viewModel.user.observe(this){
    name.text = it.name
}

突然变得非常自然。

它解决:

  • 生命周期自动管理
  • 自动取消观察
  • 数据变化自动通知 UI

当时来看,这是非常大的进步。


二、LiveData 最大的价值:让 Android 有了生命周期感知的数据观察

LiveData 最大的设计:

kotlin 复制代码
observe(owner){

}

它知道:

复制代码
STARTED

RESUMED

DESTROYED

状态。

例如:

Activity:

复制代码
onStart

↓

开始接收数据


onDestroy

↓

自动解绑

开发者不用再手动处理:

  • 取消监听
  • 防止内存泄漏
  • 判断页面状态

所以 LiveData 的核心贡献:

它解决了 Android 世界中"数据观察"和"生命周期管理"的问题。


三、但是 LiveData 的第一个限制:它不是一个真正的数据流

这是 LiveData 最大的问题之一。

LiveData:

kotlin 复制代码
val user = MutableLiveData<User>()

它更像:

一个可以被观察的数据容器。

它保存:

复制代码
当前值

然后通知:

复制代码
观察者

但是现代应用很多场景,本质是:

数据流。

例如搜索:

用户输入:

css 复制代码
a

↓

ab

↓

abc

我们希望:

复制代码
300ms没有输入

↓

请求abc

Flow:

kotlin 复制代码
queryFlow
    .debounce(300)
    .flatMapLatest {

        search(it)

    }

非常自然。

因为 Flow 表达:

复制代码
数据产生

↓

数据转换

↓

数据过滤

↓

数据消费

而 LiveData 更关注:

复制代码
现在是什么值

四、LiveData 缺少完整的数据流处理能力

现代应用大量需要:

  • map
  • filter
  • combine
  • debounce
  • retry
  • catch
  • flatMapLatest

Flow:

kotlin 复制代码
userFlow
    .filter {

    }
    .map {

    }
    .combine(otherFlow){

    }

这些都是 Flow 的核心能力。


LiveData 虽然有:

kotlin 复制代码
Transformations.map()
Transformations.switchMap()

但是能力有限。

复杂一点:

例如:

diff 复制代码
用户信息

+

配置

+

网络状态

+

权限状态

LiveData 通常:

kotlin 复制代码
MediatorLiveData

多个数据源手动管理。

代码容易越来越复杂。


五、LiveData 的第二个限制:它不适合作为 Data 层的数据类型

这是很多项目后期遇到的问题。

早期很多项目:

kotlin 复制代码
class UserRepository {
    fun getUser():LiveData<User>
}

当时觉得:

很方便。

但是问题来了:

LiveData 属于:

kotlin 复制代码
androidx.lifecycle.LiveData

它本质是 Android 生命周期组件。


Data 层为什么不应该暴露 LiveData?

因为数据层关注:

复制代码
数据怎么产生

数据怎么变化

而不是:

复制代码
Activity有没有显示

Fragment有没有销毁

但是 LiveData 天生关注:

复制代码
LifecycleOwner

例如:

复制代码
STARTED

RESUMED

DESTROYED

这意味着:

数据层开始知道:

Android 生命周期模型。


举个例子:

数据库:

kotlin 复制代码
@Query(
"select * from user"
)
fun observeUser():LiveData<User>

看起来很好。

但是如果:

后台 Service 需要用户数据:

或者:

Worker 需要监听数据:

它们怎么办?

因为它们并没有:

kotlin 复制代码
LifecycleOwner

于是开始出现:

各种转换:

kotlin 复制代码
asFlow()

其实:

如果数据源一开始就是 Flow:

这些问题不存在。


六、LiveData 会限制数据的消费方式

假设:

Repository:

kotlin 复制代码
fun message():LiveData<Message>

UI:

没问题:

kotlin 复制代码
observe()

但是:

后台任务:

kotlin 复制代码
Coroutine

更希望:

kotlin 复制代码
collect()

但是 LiveData:

设计目标就是:

Observer。

于是:

数据生产者决定了消费者模式。


而 Flow:

kotlin 复制代码
fun message():Flow<Message>

消费者自己决定:

UI:

kotlin 复制代码
collectAsState()

后台:

kotlin 复制代码
collect()

测试:

kotlin 复制代码
first()

同一个数据源:

不同消费方式。


七、LiveData 对事件处理一直比较尴尬

经典问题:

一次性事件。

例如:

登录成功:

kotlin 复制代码
loginSuccess.value = true

页面:

kotlin 复制代码
observe {

    Toast.makeText(
        "登录成功"
    )

}

旋转屏幕。

LiveData 会重新发送:

arduino 复制代码
true

结果:

Toast 又出现一次。

于是出现:

kotlin 复制代码
SingleLiveEvent

或者:

kotlin 复制代码
EventWrapper

但是这也说明:

LiveData 更适合:

状态。

不适合:

事件流。


事件:

复制代码
点击一次

发送一次

消费一次

状态:

复制代码
当前是什么

两者语义不同。


八、LiveData 的线程模型比较简单,也限制了能力

LiveData:

kotlin 复制代码
setValue()

主线程。

后台:

kotlin 复制代码
postValue()

看起来简单。

但是:

它隐藏了一些细节。

例如:

kotlin 复制代码
postValue(1)

postValue(2)

postValue(3)

最终:

可能只收到:

复制代码
3

原因:

LiveData 的目标:

保存最新状态。

它不是消息队列。


但是很多场景需要:

复制代码
事件1

事件2

事件3

全部处理

例如:

  • 下载进度
  • 消息通知
  • 操作事件

LiveData 并不适合。


九、LiveData 和 Kotlin 协程结合,不如 Flow 自然

现在 Android 主流:

css 复制代码
Coroutine

+

Flow

但是 LiveData:

通常:

kotlin 复制代码
viewModelScope.launch {
      val result = repository.getUser()
       liveData.value = result
}

中间:

python 复制代码
Coroutine

↓

LiveData

↓

Observer

多了一层。


Flow:

kotlin 复制代码
viewModelScope.launch {

    repository.user()
        .collect {

        }

}

天然连接。


十、LiveData 最大的问题:简单很好,复杂容易失控

LiveData:

简单页面:

kotlin 复制代码
val loading =
    MutableLiveData<Boolean>()

非常舒服。

但是复杂页面:

多个 LiveData:

diff 复制代码
UserLiveData

+

ConfigLiveData

+

NetworkLiveData

+

PermissionLiveData

最后:

vbnet 复制代码
MediatorLiveData

Transformations

Event包装

各种工具类

越来越像:

自己实现一个数据流框架。


十一、LiveData 和 Flow,本质区别是什么?

LiveData:

一个生命周期感知的数据容器。

Flow:

一个可以被组合、转换、取消的数据流。


LiveData 思维:

复制代码
数据变化了

通知我

Flow 思维:

复制代码
数据本身就是流

我要处理这个流

总结

LiveData 没有错。

它是 Android 架构发展过程中非常重要的一环。

它解决:

生命周期安全的数据观察。

但是它也有明显限制:

  1. 它不是完整的数据流模型

  2. 数据组合能力有限

  3. 不适合作为 Data 层的数据类型

  4. 和协程结合不够自然

  5. 对事件处理存在天然缺陷

所以:

LiveData 适合 UI 层,但不应该成为整个应用的数据传递模型。

相关推荐
千里马学框架21 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台21 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui