Room + Flow 完整教程(现代 Android 官方方案)
2026/5/15 0:34:12 网站建设 项目流程

Room + Flow 完整教程(现代 Android 官方方案)

现代 Android 开发中:

Room + Flow + Compose/ViewModel

已经是官方推荐数据库架构。

真正强大的地方是:

数据变化 → UI 自动刷新

不需要:

  • notifyDataSetChanged()
  • 手动刷新
  • 回调通知

这一篇会讲:

  • Room 是什么
  • Flow 为什么适合数据库
  • Room + Flow 工作原理
  • MVVM 实战
  • Compose 配合
  • 自动刷新机制
  • 企业级最佳实践

一、Room 是什么?

Room 是 Android 官方数据库 ORM。

底层:SQLite

但 Room 帮你:

  • 自动建表
  • 自动 SQL 映射
  • 自动线程检查
  • 自动 Flow 更新

二、为什么 Room 要配合 Flow?

传统数据库:

vallist=dao.getUsers()

问题:数据库变了,UI 不会自动更新

你得:手动刷新、LiveData、回调。很麻烦。


三、Flow 的核心优势

使用Flow<List<User>>后:

  • 数据库变化
  • Flow 自动重新发送数据
  • UI 自动刷新

这就是:响应式数据库


四、添加依赖

// Roomimplementation"androidx.room:room-runtime:2.6.1"ksp"androidx.room:room-compiler:2.6.1"// Kotlin 扩展implementation"androidx.room:room-ktx:2.6.1"// Flow 协程implementation"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"

五、Entity(数据表)

@Entity(tableName="user")dataclassUser(@PrimaryKeyvalid:Int,valname:String,valage:Int)

六、DAO(最核心)

DAO = Data Access Object,数据库操作入口。


七、最重要的 Flow 查询

@DaointerfaceUserDao{@Query("SELECT * FROM user")fungetUsers():Flow<List<User>>}

重点:返回Flow


八、为什么不用 suspend?

suspendFlow
suspend fun getUsers()Flow<List<User>>
只返回一次持续监听数据库变化

九、Room 自动更新原理(重点)

这是 Room 最强大的地方。

Room 内部维护:InvalidationTracker

数据库变化 ↓ 通知 Flow ↓ Flow:重新 emit 最新数据 ↓ UI:自动刷新

十、完整流程图(非常重要)

数据库 insert/update/delete ↓ Room InvalidationTracker ↓ Flow emit 新数据 ↓ collectAsState 收到 ↓ Compose 重组 ↓ UI自动刷新

这就是:现代 Android 响应式数据库架构


十一、插入数据

@Insertsuspendfuninsert(user:User)

十二、删除数据

@Deletesuspendfundelete(user:User)

十三、更新数据

@Updatesuspendfunupdate(user:User)

插入数据、删除、更新,为什么用suspend,不用Flow

Flow 本质是持续的数据流(Stream)

它适合:

未来还会不断变化的数据

例如:

数据库变化 Socket消息 搜索输入 传感器数据 聊天消息

插入、删除、更新操作并没有“流”

看:

@Insertsuspendfun insert(user: User)

插入:

执行一次 结束

它没有:

后续数据

所以:Flow 没意义

如果 insert 用 Flow 会怎样?

理论上你可以:

fun insert(user: User): Flow<Unit>

但:

完全没必要

因为:

它只会:

emit 一次 结束

这其实退化成:suspend

面试高频回答(建议背下来)

为什么 Room 查询用 Flow,而插入删除更新用 suspend?

标准答案:

因为查询是持续变化的数据源,需要响应式监听数据库变化,所以适合使用 Flow。 而插入、删除、更新属于一次性操作,只需要执行并返回一次结果,因此使用suspend更合适。

十四、Database

@Database(entities=[User::class],version=1)abstractclassAppDatabase:RoomDatabase(){abstractfunuserDao():UserDao}

十五、创建数据库

valdb=Room.databaseBuilder(context,AppDatabase::class.java,"app.db").build()

十六、Repository(标准架构)

classUserRepository(privatevaldao:UserDao){fungetUsers()=dao.getUsers()suspendfuninsert(user:User){dao.insert(user)}}

十七、为什么需要 Repository?

因为:ViewModel 不应该直接操作数据库,否则:

  • 强耦合
  • 不好测试
  • 架构混乱

十八、ViewModel + StateFlow

现代标准方案。

classUserViewModel(privatevalrepository:UserRepository):ViewModel(){valusers=repository.getUsers().stateIn(scope=viewModelScope,started=SharingStarted.WhileSubscribed(5000),initialValue=emptyList())funaddUser(){viewModelScope.launch{repository.insert(User(1,"Tom",18))}}}

十九、stateIn 是什么?

作用:Flow → StateFlow

因为:Compose 更适合 StateFlow。


二十、Compose 收集数据库

@ComposablefunUserPage(vm:UserViewModel){valusersbyvm.users.collectAsState()LazyColumn{items(users){Text(it.name)}}}

二十一、真正的神奇之处

调用insert()后,UI 自动刷新,完全不用notifyDataSetChanged()


二十二、为什么能自动刷新?

Room ↓ Flow ↓ collectAsState ↓ Recomposition

形成:响应式链路


二十三、collectAsStateWithLifecycle(推荐)

官方更推荐collectAsStateWithLifecycle()

依赖

implementation"androidx.lifecycle:lifecycle-runtime-compose"

使用

valusersbyvm.users.collectAsStateWithLifecycle()

为什么推荐它?自动处理生命周期,页面不可见自动停止收集。


二十四、Room 查询线程问题

suspend 查询

@Query("SELECT * FROM user")suspendfungetUsers():List<User>

Room 自动后台线程执行。

Flow 查询

Flow<List<User>>

也自动后台线程。

所以:不需要withContext(IO),这是 Room 帮你做好的。


二十五、Flow 为什么特别适合数据库?

因为数据库本质就是:持续变化的数据源

Flow 天生适合:观察变化


二十六、多个表联合查询

dataclassUserWithArticles(@Embeddedvaluser:User,@Relation(parentColumn="id",entityColumn="userId")valarticles:List<Article>)

二十七、事务查询

@Transaction@Query("SELECT * FROM user")fungetUserWithArticles():Flow<List<UserWithArticles>>

二十八、Flow 防抖搜索(经典)

DAO

@Query("SELECT * FROM user WHERE name LIKE '%' || :keyword || '%'")funsearch(keyword:String):Flow<List<User>>

ViewModel

valkeyword=MutableStateFlow("")valusers=keyword.debounce(300).flatMapLatest{dao.search(it)}

二十九、flatMapLatest 为什么重要?

因为输入变化时:

  • 取消旧查询
  • 启动新查询

避免:搜索请求堆积


三十、Room + Paging3

现代大列表方案。

DAO

@Query("SELECT * FROM user")funpagingSource():PagingSource<Int,User>

Pager

Pager(config=PagingConfig(20)){dao.pagingSource()}.flow

Compose 分页

valitems=vm.users.collectAsLazyPagingItems()

三十一、Room + Flow + Compose 真正完整链路

这是现代 Android 最核心架构。

执行流程:

Room(SQLite) ↓ Flow ↓ Repository ↓ ViewModel ↓ StateFlow ↓ Compose collectAsState ↓ Recomposition ↓ UI刷新

三十二、Room 常见错误

错误说明
在主线程操作数据库禁止allowMainThreadQueries()
UI 直接操作 DAO应该:Compose → ViewModel → Repository → DAO
Flow collect 泄漏flow.collect没有生命周期

正确:collectAsStateWithLifecycle()


三十三、Room vs LiveData

现在Flow已经基本替代LiveData,因为:

FlowLiveData
更强受限
支持操作符功能单一
协程统一非协程
Kotlin 原生Android 特定

三十四、Room + Flow 面试题

1. Room 为什么支持自动刷新?

因为:InvalidationTracker

2. Flow 和 suspend 查询区别?

suspendFlow
一次返回持续监听
单次数据数据流

3. Room 查询为什么不卡主线程?

Room 自动线程调度。

4. 为什么推荐 StateFlow?

因为:Compose 状态驱动

5. Flow 为什么适合数据库?

因为数据库是持续变化的数据源


三十五、企业级最佳实践(非常重要)

标准架构:

Compose UI ↓ ViewModel ↓ StateFlow ↓ Repository ↓ Room DAO ↓ SQLite

三十六、真正理解 Room + Flow

以前 Android:

数据库变化 ↓ 手动通知UI ↓ RecyclerView刷新

现代 Android:

数据库变化 ↓ Flow自动发射 ↓ Compose自动重组 ↓ UI自动刷新

三十七、真正的大脑模型(最重要)

看到Flow<List<User>>,脑子里自动出现:

数据库监听器 ↓ 数据变化自动emit ↓ UI自动刷新

看到collectAsState(),自动想到:

Flow → State → Compose重组

看到stateIn(viewModelScope),自动想到:

冷Flow → 热StateFlow → UI状态共享

三十八、最后一句(现代 Android 的本质)

现在 Android 官方整个方向:

已经从:命令式UI

变成:响应式状态驱动

核心链路:

数据库 ↓ Flow ↓ StateFlow ↓ Compose ↓ 自动UI刷新

这就是现代 Android 架构真正的核心

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询