资讯动态

【Jetpack Compose进阶学与练】第7课:Room本地数据库基础,ViewModel结合Room做本地持久化

发布时间:2026/10/9 2:38:23 来源:尧图企业网站定制
前言前面课程的数据全部是临时保存在内存保存数据到本地数据库可以解决推荐系统内存 loss 的问题。Room是Google官方封装的SQLite ORM框架不用手写原生SQL配合ViewModel‑Flow可以监听数据库的实时变化。本节课实体Entity、Dao、Database在ViewModel中访问RoomUI层通过Flow实时读取,Compose渲染列表。前置依赖(module build.gradle)gradleplugins {id(“kotlin-kapt”)id(“androidx.room”)}dependencies {implementation “androidx.room:room-runtime:2.7.0”kapt “androidx.room:room-compiler:2.7.0”implementation “androidx.room:room-cream:2.7.0”}知识点清单Room三层核心组件Entity实体、Dao数据访问接口、Database数据库类Entity定义数据表Dao定义数据库操作接口Database数据库入口Dao返回Flow实现数据库数据实时监听数据变更自动下发FlowRepository仓库层简单概念ViewModel不要直接调用DaoViewModel拿到仓库的数据FlowCompose收集Flow渲染列表完整示例源码kotlinimport androidx.room.Daoimport androidx.room.Databaseimport androidx.room.Entityimport androidx.room.Insertimport androidx.room.PrimaryKeyimport androidx.room.Queryimport androidx.room.RoomDatabaseimport androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScopeimport kotlinx.coroutines.flow.Flowimport kotlinx.coroutines.flow.MutableStateFlowimport kotlinx.coroutines.flow.StateFlowimport kotlinx.coroutines.launchimport androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.lazy.LazyColumnimport androidx.compose.foundation.lazy.itemsimport androidx.compose.material3.Buttonimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.runtime.collectAsStateWithLifecycleimport androidx.compose.runtime.getValueimport androidx.compose.ui.Modifier//1. Entity 实体对应数据库一张表Entity(tableName “note”)data class Note(PrimaryKey(autoGenerate true)val id: Int 0,val content: String)//2. Dao 数据访问接口Daointerface NoteDao {Query(“SELECT * FROM note ORDER BY id DESC”)fun observeAllNote(): FlowListInsert suspend fun insertNote(note: Note) Query(DELETE FROM note WHERE id:noteId) suspend fun deleteNote(noteId: Int)}//3. DatabaseDatabase(entities [Note::class], version 1)abstract class AppDatabase : RoomDatabase() {abstract fun noteDao(): NoteDao}//简易仓库层 Repositoryclass NoteRepository(private val noteDao: NoteDao) {fun observeAllNotes(): FlowList noteDao.observeAllNote()suspend fun addNote(note: Note) noteDao.insertNote(note)suspend fun removeNote(noteId: Int) noteDao.deleteNote(noteId)}class NoteViewModel : ViewModel() {private val _noteListState MutableStateFlowList(emptyList())val noteListState: StateFlowList _noteListStatefun subscribeDb(repo: NoteRepository) { viewModelScope.launch { repo.observeAllNotes().collect { list - _noteListState.value list } } } fun addNewNote(repo: NoteRepository, text: String) { viewModelScope.launch { repo.addNote(Note(content text)) } } fun deleteNote(repo: NoteRepository, id: Int) { viewModelScope.launch { repo.removeNote(id) } }}Composablefun NoteListScreen(vm: NoteViewModel, repo: NoteRepository) {val noteList by vm.noteListState.collectAsStateWithLifecycle()Column(modifier Modifier.fillMaxSize()) { Button(onClick { vm.addNewNote(repo, 新建一条笔记 ${System.currentTimeMillis()}) }) { Text(➕新增笔记) } LazyColumn { items(noteList) { item - Column { Text(text [${item.id}] ${item.content}) Button(onClick { vm.deleteNote(repo, item.id) }) { Text(删除) } } } } }}逐行解读Room三大核心Entity(Entity)映射数据库数据表data class字段对应表里面的列PrimaryKey设置主键autoGeneratetrue自增。Dao(Dao)数据访问接口写数据库的增删改查注解suspend修饰的函数适合协程里面调用查询返回Flow之后数据库数据一旦发生变化Flow会自动发出新的数据实现实时监听。Database(Database)抽象类继承RoomDatabase向外提供Dao抽象方法项目运行时kapt自动生成实现代码。Repository仓库层ViewModel不要直接访问Dao中间增加一层Repository仓库仓库专门封装数据库访问逻辑以后数据源可以切换本地数据库 / 网络APIViewModel不需要改动解耦UI业务和数据库。Dao返回Flow实时监听数据库fun observeAllNote():FlowList 不需要手动刷新只要表里面的数据新增或者删除Flow就自动下发最新笔记列表ViewModel收集Flow之后更新内部StateFlow驱动Compose页面自动刷新列表。suspend关键字增删改方法标记 suspend 只能在协程里面调用禁止在主线程执行数据库读写避免ANR卡顿。初始化Database注意示例代码省略初始化实际项目需要通过Room.databaseBuilder()构造AppDatabase实例再基于db拿到Dao构造Repository再传给ViewModel不要在ViewModel内部创建数据库实例数据库实例建议Application中初始化单例。容易踩坑❌ 数据库读写放在主线程执行直接触发ANR增删改必须suspend协程里面调用。❌ 忘记kapt插件Room注解处理器无法生成数据库实现代码编译直接报错。❌ ViewModel内部new RoomDatabase数据库不应该由ViewModel创建应当Application层初始化全局单例。✅ Dao查询返回Flow自动监听表数据变更Repository隔离数据库ViewModel调用Repository单向数据流更新UI。多元化习题练习习题1【基础‑改错题】开发直接在ViewModel里面new AppDatabase()创建数据库实例有什么问题答案ViewModel生命周期跟随页面页面销毁ViewModel就销毁如果每次页面打开都新建数据库实例资源反复创建释放数据库应当在Application初始化全局单例。解读数据库实例是重量级对象整个App复用同一个数据库连接实例。习题2【基础‑代码修改题】笔记Dao新增根据id查询单条笔记的suspend查询方法SQLSELECT * FROM note WHERE id:nId写出Dao里面的函数。答案kotlinQuery(“SELECT * FROM note WHERE id:nId”)suspend fun findNoteById(nId:Int):Note?解读单条查询使用suspend挂起不需要Flow只有需要持续监听数据变化的时候才返回Flow。习题3【进阶‑补充代码题】说明Dao返回Flow查询和suspend一次性查询二者适用场景。答案Dao返回Flow适合页面列表需要实时响应数据库变更一旦表数据改动自动下发新列表UI自动刷新。suspend一次性查询打开详情页面只读取一次数据后续不需要持续监听使用suspend。解读Flow会持续订阅数据库页面退出订阅要取消ViewModel销毁viewModelScope会自动终止收集。习题4【综合‑逻辑应用题】为什么要引入Repository仓库层ViewModel不直接调用Dao答案Repository作为数据中间层ViewModel只关心业务Repository内部可以同时封装Room本地数据库和Retrofit网络后续切换数据源ViewModel代码不需要改动实现业务层和数据源解耦。解读分层架构UI‑ViewModel‑Repository‑Dao(Room)/Network。习题5【概念简答题】Room的Flow查询为什么可以自动感知数据库表的数据变更答案Room内部会监听对应的数据表当该表发生insert、delete、updateRoom就会重新执行这条查询SQL并且把新的查询结果发送到Flow上层收集Flow就拿到最新列表。解读这是Room非常强大的特性Compose列表页面大量使用。本节课知识点总结Room三大组件Entity实体数据表Dao访问接口Database数据库抽象类。suspend用于一次性增删改查查询返回Flow实现数据库实时监听表改动自动下发新数据。Repository仓库层隔离ViewModel与Dao上层ViewModel依赖Repository不直接接触数据库API。数据库实例应当Application初始化单例禁止ViewModel内部创建数据库。数据库读写必须协程执行防止主线程读写数据库造成ANR卡顿。下一课预告进阶第8课项目架构分层实战UI‑ViewModel‑Repository‑数据源MVI简单介绍

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑