Jetpack之Room的使用,结合Flow,android原生开发
//按类型 查询所有搜索历史@Query(“SELECT * FROM t_history WHERE type=:type”)fun getAll(type: Int = 1): Flow<List>@ExperimentalCoroutinesApifun getAllDistinctUntilChanged() = getAll().distinctUntilChanged()/
//按类型 查询所有搜索历史
@Query(“SELECT * FROM t_history WHERE type=:type”)
fun getAll(type: Int = 1): Flow<List>
@ExperimentalCoroutinesApi
fun getAllDistinctUntilChanged() = getAll().distinctUntilChanged()
//添加一条搜索历史
@Insert
fun insert(history: History)
//删除一条搜索历史
@Delete
fun delete(history: History)
//更新一条搜索历史
@Update
fun update(history: History)
//根据id 删除一条搜索历史
@Query(“DELETE FROM t_history WHERE id = :id”)
fun deleteByID(id: Int)
//删除所有搜索历史
@Query(“DELETE FROM t_history”)
fun deleteAll()
}
-
@Insert:增
-
@Delete:删
-
@Update:改
-
@Query:查
这里有一个点需要注意的,就是查询所有搜索历史返回的集合我用Flow修饰了。
只要是数据库中的任意一个数据有更新,无论是哪一行数据的更改,那就重新执行 query操作并再次派发Flow。
同样道理,如果一个不相关的数据更新时,Flow也会被派发,会收到与之前相同的数据。
这是因为 SQLite 数据库的内容更新通知功能是以表 (Table) 数据为单位,而不是以行 (Row) 数据为单位,因此只要是表中的数据有更新,它就触发内容更新通知。Room 不知道表中有更新的数据是哪一个,因此它会重新触发 DAO 中定义的 query 操作。您可以使用 Flow 的操作符,比如 distinctUntilChanged 来确保只有在当您关心的数据有更新时才会收到通知。
//按类型 查询所有搜索历史
@Query(“SELECT * FROM t_history WHERE type=:type”)
fun getAll(type: Int = 1): Flow<List>
@ExperimentalCoroutinesApi
fun getAllDistinctUntilChanged() = getAll().distinctUntilChanged()
数据库
@Database(entities = [History::class], version = 1)
abstract class HistoryDatabase : RoomDatabase() {
abstract fun historyDao(): HistoryDao
companion object {
private const val DATABASE_NAME = “history.db”
private lateinit var mPersonDatabase: HistoryDatabase
//注意:如果您的应用在单个进程中运行,在实例化 AppDatabase 对象时应遵循单例设计模式。
//每个 RoomDatabase 实例的成本相当高,而您几乎不需要在单个进程中访问多个实例
fun getInstance(context: Context): HistoryDatabase {
if (!this::mPersonDatabase.isInitialized) {
//创建的数据库的实例
mPersonDatabase = Room.databaseBuilder(
context.applicationContext,
HistoryDatabase::class.java,
DATABASE_NAME
).build()
}
return mPersonDatabase
}
}
}
-
使用
@Database注解声明 -
entities数组,对应此数据库中的所有表 -
version数据库版本号
注意:
如果您的应用在单个进程中运行,在实例化 AppDatabase 对象时应遵循
单例设计模式。 每个 RoomDatabase
实例的成本相当高,而您几乎不需要在单个进程中访问多个实例。
=============================================================
在需要的地方获取数据库
mHistoryDao = HistoryDatabase.getInstance(this).historyDao()
获取搜索历史
private fun getSearchHistory() {
MainScope().launch(Dispatchers.IO) {
mHistoryDao.getAll().collect {
withContext(Dispatchers.Main){
//更新ui
}
}
}
}
collect 是Flow获取数据的方式,并不是唯一方式,可以查看文档。
为什么放在协程里面呢,因为数据库的操作是费时的,而协程可以轻松的指定线程,这样不阻塞UI线程。
查看Flow源码也发现,Flow是协程包下的
package kotlinx.coroutines.flow
以collect为例,也是被suspend 修饰的,既然支持挂起,那配合协程岂不美哉。
@InternalCoroutinesApi
public suspend fun collect(collector: FlowCollector)
保存搜索记录
private fun saveSearchHistory(text: String) {
MainScope().launch(Dispatchers.IO) {
mHistoryDao.insert(History(null, text, DateUtils.longToString(System.currentTimeMillis())))
}
}
清空本地历史
private fun cleanHistory() {
MainScope().launch(Dispatchers.IO) {
mHistoryDao.deleteAll()
}
}
================================================================
数据库升级是一个重要的操作,毕竟可能会造成数据丢失,也是很严重的问题。
Room通过Migration类来执行升级的操作,我们只要告诉Migration类改了什么就行,比如新增字段或表。
定义Migration类
/**
- 数据库版本 1->2 t_history表格新增了updateTime列
*/
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(“ALTER TABLE t_history ADD COLUMN updateTime String”)
}
}
/**
- 数据库版本 2->3 新增label表
*/
private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(“CREATE TABLE IF NOT EXISTS t_label (id INTEGER PRIMARY KEY autoincrement, name TEXT)”)
}
}
Migration接收两个参数:
-
startVersion 旧版本
-
endVersion 新版本
通知数据库更新
mPersonDatabase = Room.databaseBuilder(
context.applicationContext,
HistoryDatabase::class.java,
DATABASE_NAME
).addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
完整代码
@Database(entities = [History::class, Label::class], version = 3)
abstract class HistoryDatabase : RoomDatabase() {
abstract fun historyDao(): HistoryDao
companion object {
private const val DATABASE_NAME = “history.db”
private lateinit var mPersonDatabase: HistoryDatabase
fun getInstance(context: Context): HistoryDatabase {
if (!this::mPersonDatabase.isInitialized) {
//创建的数据库的实例
mPersonDatabase = Room.databaseBuilder(
context.applicationContext,
HistoryDatabase::class.java,
DATABASE_NAME
).addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
}
return mPersonDatabase
}
/**
- 数据库版本 1->2 t_history表格新增了updateTime列
*/
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(“ALTER TABLE t_history ADD COLUMN updateTime String”)
}
}
/**
- 数据库版本 2->3 新增label表
*/
private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(“CREATE TABLE IF NOT EXISTS t_label (id INTEGER PRIMARY KEY autoincrement, name TEXT)”)
}
}
}
}
注意:
@Database注解中版本号的更改,如果是新增表的话,entities参数里也要添加上。
建议升级操作顺序
修改版本号 -> 添加Migration -> 添加给databaseBuilder
==================================================================
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持)
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)

9fa756956c4d?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料

- Android BAT大厂面试题(有解析)
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持)
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
]
9fa756956c4d?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
[外链图片转存中…(img-Ao54YPwU-1647530294087)]
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
[外链图片转存中…(img-M9ateQwm-1647530294087)]
- Android BAT大厂面试题(有解析)
[外链图片转存中…(img-b48Xw376-1647530294088)]
更多推荐

所有评论(0)