单点组件不够用
单独用 ViewModel、单独用 Room,都不难。难的是把它们串成一条稳的链路:数据怎么进库、怎么变成界面状态、界面改了怎么回写、页面之间怎么跳转。各组件若各写各的,状态很容易不同步,界面和数据各说各话。这篇讲整合。
架构组件不是各管各的,它们本来就是为串起来设计的。
这篇把 ViewModel、Room、Flow、导航整合成一条完整数据流。
分层总览
从上到下三层:界面层收集状态画界面,ViewModel 持有状态并调度,数据层由仓库和 Room 提供数据流。各层只依赖下一层,方向单一,谁也不反过来依赖谁。这种单向依赖让代码层次清楚,改动一层不容易波及其它层。
整合速查
- 数据层Room 暴露 Flow。
- ViewModel转成界面状态。
- 界面收集状态画 UI。
Room 提供数据流
Room 查询返回 Flow,数据一变它自动发新值。
@Query("SELECT * FROM items ORDER BY id DESC")
fun all(): Flow<List<Item>>
数据库成了一个会自己推送的数据源头。
ViewModel 桥接
ViewModel 把仓库的 Flow 转成界面状态,界面不用管数据怎么来的。
界面收集 Flow
界面在生命周期安全的位置收集 ViewModel 的状态,状态一变就自动刷新。配置改变ViewModel 还在,界面重建也不丢数据。
导航串起页面
用 Navigation 把页面之间的跳转画成一张图。点列表项跳到详情,参数顺着导航传过去。
- Room 推数据。
- ViewModel 转状态。
- 导航管跳转。
实战:整合一条列表
Room 出 Flow,ViewModel 转 UiState,界面收集后画列表。
class ListModel(repo: Repo) {
val state = repo.all()
.map { UiState(it) }
.stateIn(scope, SharingStarted.Lazily, UiState())
}
一条完整链路跑通:数据库变、界面跟着变。整合做到这份上,新增一页就是加一层状态映射,不用再从界面手动拉数据。数据流一旦单向贯通,排查问题也简单,沿着数据流反着追,很快就能定位到底是哪一层出了岔子。新成员接手也能顺着这条链路很快看懂全局。