一、介绍
接着上一篇Room的基本使用介绍(不会Room基本使用的先看这一篇),每次使用增删改查功能都需要new Thread,不方便也不好管理,本章主要介绍RxJava如何搭配Room使用。
二、引入RxJava库
def latest_version = "2.2.5"
//room
implementation "androidx.room:room-runtime:$latest_version"
implementation "androidx.room:room-ktx:$latest_version"
kapt "androidx.room:room-compiler:$latest_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$latest_version"
三、项目实战
使用前需要先准备数据实体,数据库,数据访问对象(DAO)。 其中数据实体和数据库的内容与上一篇的内容一样,核心在数据访问对象的修改。
数据实体
@Entity
class RunRecord {
@PrimaryKey(autoGenerate = true)
var id: Long? = null
@ColumnInfo(name = "userId")
var userId: String? = null
@ColumnInfo(name = "pathLine")
var pathLine: String? = null
@ColumnInfo(name = "totalStep")
var totalStep:Int? = 0
}
数据库
@Database(entities = [ RunRecord::class], version = 1)
abstract class YzDatabase : RoomDatabase() {
abstract fun runDao(): RunDao?
companion object {
private const val DB_NAME = "yzWill.db"
@Volatile
private var instance: YzDatabase? = null
fun getInstance(context: Context) =
instance ?: synchronized(this) {
instance ?: Room.databaseBuilder(
context,
YzDatabase::class.java, DB_NAME
).build()
}
}
}
数据访问对象
与上一篇的区别就是把方法的返回值外包装了一层Single(为什么使用Single后面说),例如:Long ->Single
@Dao
interface RunDao{
/**
*
* 插入一条跑步信息,id不用传,会自动增长.建议不传
* @param info RunRecord?
* @return Single<Long> 返回插入的主键id
*/
@Insert
fun insert(info: RunRecord?): Single<Long>
/**
* 删除某一条,需要传入主键ID 例如,:
RunRecord().apply {
this.id = 1
}
* @return Single<Int> 1 成功 0失败
*/
@Delete
fun delete(info: RunRecord?): Single<Int>
/**
* 需要传入主键ID 例如,传入id,修改userId为 "weng":
RunRecord().apply {
this.id = 1
this.userId = "weng"
}
* @return Single<Int> 1 成功 0失败
*/
@Update
fun update(vararg info: RunRecord?): Single<Int>
/**
* 根据userId获取最后一条
* @param userId String
* @return RunRecord?
*/
@Query("SELECT * FROM RunRecord WHERE userId LIKE :userId order by userId desc LIMIT 1")
fun findLastByUserId(userId: String): Single<RunRecord?>
/**
* 查询全部数据
*/
@get:Query("SELECT * FROM RunRecord")
val all: Single<List<RunRecord?>?>
}
使用
下面就举两个插入和删除的栗子
val db = YzDatabase.getInstance(this).runDao()
//插入一条数据,返回主键id
db?.insert(RunRecord().apply {
pathLine = "插入一条信息"
userId = "小小虫"
})
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.doOnSuccess {
Log.e(tag, "id:$it")
}?.subscribe()
val db = YzDatabase.getInstance(this).runDao()
//删除ID为1的数据,返回 0:删除失败 1:删除成功
db?.delete(RunRecord().apply {
id = 1
pathLine = "插入一条信息"
userId = "小小虫"
})
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.doOnSuccess {
Log.e(tag, "success:${it == 1}")
}?.subscribe()
四、封装
通过上文,我们可以虽然把Room结合了RxJava,但用起来很明显有很多冗余的代码,可以简单的封装一下。增加一个DBManager类,如下:
class DBManager(context: Context) {
private var mDB: YzDatabase? = YzDatabase.getInstance(context)
companion object {
@Volatile
private var instance: DBManager? = null
fun getInstance(context: Context) =
instance ?: synchronized(this) {
instance ?: DBManager(context)
}
}
fun insertRunRecord(info: RunRecord): Single<Long>? {
return extra(mDB?.runDao()?.insert(info))
}
fun findLastByUserId(userId: String): Single<RunRecord?>? {
return extra(mDB?.runDao()?.findLastByUserId(userId))
}
private fun <T> extra(single: Single<T>?): Single<T>? {
return single
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
}
}
使用
可以看到,代码量明显的减少。
DBManager.getInstance(this).insertRunRecord(RunRecord().apply {
pathLine = "插入一条信息"
userId = "小小虫"
}) ?.doOnSuccess {
Log.e(tag, "id:$it")
}?.subscribe()
五、踩坑
先看一段查询数据库的代码,如下:
DBManager.getInstance(this).findLastByUserId(userId = "小小虫")
?.doOnSuccess {
Log.e(tag, "info:$it")
}?.subscribe()
一段很简单的代码,但问题是,如果你查到数据了,一切都好。如果查不到,对不起,App闪退,报错如下:

解决方法如下:
- 不要使用RxJava(。。。),也就是用最原始的方法,new Thread去查询。
- 不要使用Single接收,使用**Maybe(推荐)**或者Flowable、Observable。
- 如果你还要用Single,那么请在包一层List,也就是Single<List>。
看下官方的解释:
至于这个问题的原因,其实跟Single的特性相关,因为Single本身的设计就是必须发出单个非空值的类型。
为什么使用Single?
RxJava的被观察者有五个,如下:
- Observable: 用来发射0或者多个数据
- Flowable: Observable的升级版,增加了背压策略
- Single: 用来发射单个数据onSuccess或错误onError事件
- Maybe: 用来发射0或者单个数据
- Completable: 不发射数据,Room好像也不支持()
可以看到,数据库的增加改查都是需要0次或者单次的结果返回的,那么就只需要在Single和Maybe中选择。当然,你使用Observable和Flowable也是可以的,这里推荐使用Maybe来作为查询数据的返回,效率相比其他两种会比较快。
版权声明:本文为wumeixinjiazu原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。