//按类型 查询所有搜索历史

@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

}

}

}

}

collectFlow获取数据的方式,并不是唯一方式,可以查看文档

为什么放在协程里面呢,因为数据库的操作是费时的,而协程可以轻松的指定线程,这样不阻塞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()

}

}

作者:https://blog.csdn.net/yechaoa

数据库升级

================================================================

数据库升级是一个重要的操作,毕竟可能会造成数据丢失,也是很严重的问题。

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大板块)

![](https://upload-images.jianshu.io/upload_images/22459598-37ca

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

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持)

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

![](https://upload-images.jianshu.io/upload_images/22459598-37ca

[外链图片转存中…(img-4GKPGT4d-1647530294086)]

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

[外链图片转存中…(img-Ao54YPwU-1647530294087)]

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

[外链图片转存中…(img-M9ateQwm-1647530294087)]

  • Android BAT大厂面试题(有解析)

[外链图片转存中…(img-b48Xw376-1647530294088)]

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐