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

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?

传统数据库:

kotlin 复制代码
val list = dao.getUsers()

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

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


三、Flow 的核心优势

使用 Flow<List<User>> 后:

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

这就是:响应式数据库


四、添加依赖

kotlin 复制代码
// Room
implementation "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(数据表)

kotlin 复制代码
@Entity(tableName = "user")
data class User(
    @PrimaryKey
    val id: Int,
    val name: String,
    val age: Int
)

六、DAO(最核心)

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


七、最重要的 Flow 查询

kotlin 复制代码
@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getUsers(): Flow<List<User>>
}

重点: 返回 Flow


八、为什么不用 suspend?

suspend Flow
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 响应式数据库架构


十一、插入数据

kotlin 复制代码
@Insert
suspend fun insert(user: User)

十二、删除数据

kotlin 复制代码
@Delete
suspend fun delete(user: User)

十三、更新数据

kotlin 复制代码
@Update
suspend fun update(user: User)

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

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

它适合:

bash 复制代码
未来还会不断变化的数据

例如:

bash 复制代码
数据库变化
Socket消息
搜索输入
传感器数据
聊天消息

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

看:

bash 复制代码
@Insert
suspend fun insert(user: User)

插入:

bash 复制代码
执行一次
结束

它没有:

bash 复制代码
后续数据

所以:Flow 没意义

如果 insert 用 Flow 会怎样?

理论上你可以:

bash 复制代码
fun insert(user: User): Flow<Unit>

但:

bash 复制代码
完全没必要

因为:

它只会:

bash 复制代码
emit 一次
结束

这其实退化成:suspend

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

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

标准答案:

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

十四、Database

kotlin 复制代码
@Database(
    entities = [User::class],
    version = 1
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

十五、创建数据库

kotlin 复制代码
val db = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "app.db"
).build()

十六、Repository(标准架构)

kotlin 复制代码
class UserRepository(private val dao: UserDao) {
    fun getUsers() = dao.getUsers()

    suspend fun insert(user: User) {
        dao.insert(user)
    }
}

十七、为什么需要 Repository?

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

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

十八、ViewModel + StateFlow

现代标准方案。

kotlin 复制代码
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    val users = repository.getUsers()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )

    fun addUser() {
        viewModelScope.launch {
            repository.insert(User(1, "Tom", 18))
        }
    }
}

十九、stateIn 是什么?

作用: Flow → StateFlow

因为:Compose 更适合 StateFlow。


二十、Compose 收集数据库

kotlin 复制代码
@Composable
fun UserPage(vm: UserViewModel) {
    val users by vm.users.collectAsState()

    LazyColumn {
        items(users) {
            Text(it.name)
        }
    }
}

二十一、真正的神奇之处

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


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

复制代码
Room
    ↓
Flow
    ↓
collectAsState
    ↓
Recomposition

形成:响应式链路


二十三、collectAsStateWithLifecycle(推荐)

官方更推荐 collectAsStateWithLifecycle()

依赖

kotlin 复制代码
implementation "androidx.lifecycle:lifecycle-runtime-compose"

使用

kotlin 复制代码
val users by vm.users.collectAsStateWithLifecycle()

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


二十四、Room 查询线程问题

suspend 查询

kotlin 复制代码
@Query("SELECT * FROM user")
suspend fun getUsers(): List<User>

Room 自动后台线程执行。

Flow 查询

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

也自动后台线程。

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


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

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

Flow 天生适合:观察变化


二十六、多个表联合查询

kotlin 复制代码
data class UserWithArticles(
    @Embedded
    val user: User,
    @Relation(
        parentColumn = "id",
        entityColumn = "userId"
    )
    val articles: List<Article>
)

二十七、事务查询

kotlin 复制代码
@Transaction
@Query("SELECT * FROM user")
fun getUserWithArticles(): Flow<List<UserWithArticles>>

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

DAO

kotlin 复制代码
@Query("SELECT * FROM user WHERE name LIKE '%' || :keyword || '%'")
fun search(keyword: String): Flow<List<User>>

ViewModel

kotlin 复制代码
val keyword = MutableStateFlow("")

val users = keyword
    .debounce(300)
    .flatMapLatest {
        dao.search(it)
    }

二十九、flatMapLatest 为什么重要?

因为输入变化时:

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

避免: 搜索请求堆积


三十、Room + Paging3

现代大列表方案。

DAO

kotlin 复制代码
@Query("SELECT * FROM user")
fun pagingSource(): PagingSource<Int, User>

Pager

kotlin 复制代码
Pager(
    config = PagingConfig(20)
) {
    dao.pagingSource()
}.flow

Compose 分页

kotlin 复制代码
val items = 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,因为:

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

三十四、Room + Flow 面试题

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

因为:InvalidationTracker

2. Flow 和 suspend 查询区别?

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

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 架构真正的核心

相关推荐
泡泡以安1 小时前
Unidbg学习笔记(八):文件系统层补环境
android·逆向
泡泡以安1 小时前
Unidbg学习笔记(六):补环境的思维框架
android·逆向
通往曙光的路上2 小时前
mysql2
android·adb
木易 士心2 小时前
会见SDK文档
android
Co_Hui3 小时前
Android:多线程
android
赏金术士3 小时前
Kotlin 协程面试题大全(Android 高频版)
android·开发语言·kotlin
y小花3 小时前
DRM-Direct Rendering Manager
android·drm
特立独行的猫a3 小时前
鸿蒙 PC 命令行工具迁移实战 · 直播PPT
android·华为·harmonyos·vcpkg·三方库移植·鸿蒙pc
赏金术士3 小时前
Kotlin 协程底层原理(Continuation)详解
java·开发语言·kotlin