Android 架构这些年,其实一直在消灭碎片化异步

工作几年才发现:

text 复制代码
真正折磨人的根本不是线程。

而是这些东西:

  • callback 一层套一层
  • Activity 都退出了任务还在跑
  • 页面销毁了回调突然回来了
  • 数据更新顺序乱掉
  • Handler、Rx、LiveData、Flow 混着用
  • 每个 SDK 都有自己的一套回调体系

最后项目慢慢变成:

一个巨大的异步缝合怪。

而这,其实才是 Android 大型项目真正的复杂度来源。


一、Android 最早其实没有统一异步方案

回头看看 Android 的发展历史,你会发现:

Android 从来都不缺异步方案。

缺的是:

一个统一的异步方案。


Thread + Handler 时代

最早大家都是这么写:

kotlin 复制代码
Thread {
    val data = request()

    handler.post {
        updateUi(data)
    }
}.start()

刚学的时候觉得:

text 复制代码
好简单啊。

后来项目大了以后发现:

  • 线程谁创建?
  • 谁销毁?
  • 谁切主线程?
  • 页面退出怎么办?
  • 回调回来页面没了怎么办?

写着写着就开始出现:

text 复制代码
if(activity.isFinishing) return
text 复制代码
if(fragment.isAdded)
text 复制代码
WeakReference<Activity>

整个代码开始变得越来越奇怪,也越来越难维护。


AsyncTask 时代

后来 Google 觉得:

text 复制代码
直接操作 Thread 太难了。

于是推出了:

kotlin 复制代码
doInBackground()

onPostExecute()

当时感觉简直就是神器。

结果几年后大家发现:

它其实只是:

text 复制代码
把 callback 换了个地方写。

问题一个都没解决。

最后甚至被官方直接废弃。


Listener 大爆炸时代

后来 Android 系统越来越复杂。

各种 Listener 开始疯狂增长:

  • LocationListener
  • SensorListener
  • BluetoothListener
  • MediaPlayerListener
  • CameraListener
  • SurfaceListener

然后你会发现:

每个系统组件都有自己的玩法。

有的:

text 复制代码
回调主线程

有的:

text 复制代码
回调 Binder 线程

有的:

text 复制代码
必须手动 unregister

有的:

text 复制代码
生命周期自动结束

还有一些第三方 SDK:

text 复制代码
文档都不告诉你在哪个线程回调。

于是整个项目开始进入:

万物皆回调时代。


二、真正复杂的,是线程之间的协作。

很多人觉得:

text 复制代码
异步难 = 多线程难

其实完全不是。

举个最常见的业务:

text 复制代码
请求用户信息
↓

拿到用户 ID

↓

请求订单列表

↓

写入数据库

↓

通知 UI 更新

↓

页面退出时自动取消

这里最难的地方其实根本不是:

text 复制代码
开线程。

而是:

text 复制代码
这一整套流程谁来管理?

什么时候取消?

异常怎么传?

页面销毁怎么办?

谁负责切线程?

谁负责回调 UI?

这些才是真正麻烦的地方。


三、最可怕的是每层都有自己的异步模型

比如一个老项目可能长这样:

网络层:

text 复制代码
Callback

数据库:

text 复制代码
RxJava

UI:

text 复制代码
LiveData

系统事件:

text 复制代码
Handler

IM:

text 复制代码
Listener

最后业务代码变成:

text 复制代码
Callback 套 Rx

Rx 转 LiveData

LiveData 再转 Flow

Flow 最后再 collect

整个项目像一个异步中转站。

你根本不知道:

数据到底从哪里来的。

又会在什么时候回来。


四、RxJava 第一次让大家看到希望

RxJava 当年为什么这么火?

因为它第一次告诉大家:

text 复制代码
别管什么网络请求、按钮点击、数据库变化。

统统都是流。

网络请求是流。

数据库变化是流。

按钮点击也是流。

甚至定时器都是流。

突然之间:

整个项目终于开始说同一种语言了。

这也是为什么:

那几年 Android 基本就是:

text 复制代码
不会 RxJava 都不好意思投简历。

五、但 RxJava 最大的问题是:

它终究只是一个库。

换句话说:

text 复制代码
你会 Rx 才能玩。

不会?

那项目基本看不懂。

尤其这种代码:

kotlin 复制代码
flatMap()
switchMap()
zip()
combineLatest()
observeOn()
subscribeOn()

新人看到基本直接投降。

而且:

Rx 世界。

Callback 世界。

Kotlin 世界。

很多时候还是三套体系同时存在。

项目依旧会慢慢变成:

text 复制代码
异步缝合怪。

六、协程真正厉害的地方

Kotlin 协程最厉害的地方其实是:

text 复制代码
异步终于变成语言能力了,以及把复杂的异步变成线性的可控的。

以前:

text 复制代码
异步靠框架。

现在:

text 复制代码
异步直接写进语言里。

于是:

kotlin 复制代码
val user = api.getUser()

val order = api.getOrder(user.id)

dao.insert(order)

看起来像同步。

实际上全是异步。

代码终于开始重新变得像人写的了。


七、Flow 真正解决的是:

终于不用到处转来转去了。

以前:

text 复制代码
Callback -> Rx -> LiveData -> UI

现在:

text 复制代码
Flow -> UI

网络:

kotlin 复制代码
Flow<User>

数据库:

kotlin 复制代码
Flow<List<Article>>

WebSocket:

kotlin 复制代码
Flow<Message>

蓝牙状态:

kotlin 复制代码
Flow<BluetoothState>

UI 状态:

kotlin 复制代码
StateFlow<UiState>

整个项目终于开始说一种语言。

这件事其实比性能优化重要得多。

因为:

架构最怕的就是乱


八、Compose 为什么疯狂拥抱 Flow

因为它俩本来就是一家的。

Compose:

text 复制代码
状态变了
↓

UI 自动刷新

Flow:

text 复制代码
数据变了
↓

状态自动更新

所以:

kotlin 复制代码
val uiState by viewModel.uiState.collectAsState()  这里是举例,生产环境要使用正确的withlife 那个。

九、这些年 Android 架构升级,

其实一直都在解决同一个问题。

表面上看:

text 复制代码
MVVM
MVI
Compose
Flow
StateFlow
UDF

像是各种新名词不断冒出来。

但本质上它们都在做一件事:

把失控的异步重新关进规则里。

因为大型项目真正可怕的从来不是:

text 复制代码
功能太多。

而是:

text 复制代码
异步失控。

什么时候执行?

什么时候取消?

谁先回来?

谁后回来?

谁负责更新状态?

这些问题如果没人管。

项目迟早会变成:

text 复制代码
谁改谁背锅。

最后

Android 架构这些年的演化。

表面上看是在:

text 复制代码
升级 API
更换框架
更新架构

但底层其实一直都在做同一件事:

消灭异步缝合怪。

从 Thread 到 Handler。

从 AsyncTask 到 RxJava。

从 RxJava 到 Coroutine。

从 LiveData 到 Flow。

从 XML 到 Compose。

技术一直在变。

但 Android 工程师和异步战斗这件事,

十几年了,到了协程,总算熬到了头。

参考

从 Callback 到 Coroutines:Android 异步并发方案的演进

"结构化"这个词,本质上就是------把混乱的东西变成有组织、有规则、有边界的东西

从"调用方的如履薄冰"到"接口的天然语义":Room/DataStore/Retrofit 的启示

现代 Android 官方为什么更推荐 Repository 暴露 suspend fun,而不是在内部 launch

# Android 架构指南之Data 层不要再暴露 start/stop 了:用 Flow 接管生命周期

别再 launch(IO) 了:协程线程切换的 3隐藏反模式

从送外卖看Android Clean架构:为什么老板不需要知道外卖员开什么车

用一个小 Demo,带你入门安卓 Clean Architecture

Android 现代架构不需要事件总线

为什么我不在 Android ViewModel 中直接处理异常?

相关推荐
灯塔@kuaidao1 小时前
平台交叉编译名词解释与基础流程
android
__Witheart__2 小时前
3568 Android otg模式下adb热拔插不识别
android·adb·rockchip
明天…ling4 小时前
Upload-Labs (Pass1-Pass21) 完整通关思路与源码分析
android·网络安全·渗透测试·burpsuite·upload-labs·文件上传绕过·靶场复现
__Witheart__4 小时前
3568 Android ntp校时使用
android·rockchip
wddptwd285 小时前
android studio 报错怎么处理 java.lang.NullPointerException
android·java·android studio
美狐美颜SDK开放平台5 小时前
直播APP开发,美颜SDK和相机SDK有什么区别?
android·深度学习·数码相机·ios·直播美颜sdk·视频美颜sdk
唐诺7 小时前
Android Open Accessory (AOA) 协议完全解析
android·usb·aoa
我命由我123459 小时前
Android 开发 - 广播组件(标准广播、有序广播、静态注册广播、分钟到达广播、网络变更广播...)
android·java·开发语言·网络·java-ee·android studio·android-studio
A富得流油的咸鸭蛋9 小时前
安卓鸿蒙面试
android·面试·harmonyos
wardenlzr9 小时前
@IntDef 替代 enum:Android 官方推荐的轻量常量方案
android·性能