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 层,但不应该成为整个应用的数据传递模型。

相关推荐
雨白13 小时前
NDK 初探:基于 C++ 实现参数哈希与签名校验
android
程序员正茂13 小时前
Android studio中初步使用OpenCV库
android·opencv
紫_龙14 小时前
window 维护多版本Android studio
android·ide·android studio
杉氧15 小时前
KMP 自动化之路 (4):iOS 自动化构建与签名 —— 攻克最硬的骨头
android·架构·android jetpack
通玄19 小时前
Jetpack Compose 入门系列(十):Paging 3 分页加载
android
vistaup19 小时前
Android studio 历史版本
android·ide·android studio
hunterandroid19 小时前
DataStore 工程化实践:迁移、并发更新与异常恢复
android·前端
程序员-珍1 天前
报错下载android sdk失败
android·java
齊家治國平天下1 天前
AAOS 电源管理深度解析:休眠/唤醒/功耗优化
android·车载系统·aaos·aosp·电源管理·休眠唤醒·carpowermanager