Room配合RxJava2,使用方法兼使用心得兼注意事项

10 篇文章 0 订阅
4 篇文章 0 订阅

以下内容基于Room 2.1.0-alpha04

ObjectBox升级到2.3后,与正在使用中的百度的“离在线语音识别”SDK产生了严重的so native冲突会导致崩溃,这个问题实在是有心无力,正好趁此机会,换用谷爹强推的Room持久库。
本篇记录了Room配合RxJava2使用的方式及一些注意事项,从数据库操作而非Room注解的角度来总结一下Room的操作方式,其中部分信息来源都来自于Room&RxJava和Room的注释文档。

为什么Room一定要配合RxJava来使用

当然是因为用起来很爽啦

其实由于Room是不能在主线程进行数据库操作的,一在主线程操作,系统就会用java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.来教你做一名“Android Developer”,但是Room本身又没有提供切换线程的简便方法,最多也就配合LiveData可以做到自动在IO线程操作。但实际上用LiveData专门来做这种事情简直是杀鸡用牛刀,LiveData配合Room使用,本意是得到一个可观测的对象,并且这个对象会随着数据库中数据的变化不停的接收到变化后的数据,从来不停的更新UI,使UI始终展示的是数据库中的最新数据。然而根据我有限的Android开发经验来看,这种的应用场景简直少之又少,至少我是从来没遇见过的。
基于目前Android使用数据库进行“增删改查”,基本上都是一次性操作,很少有需要持续观察数据库变化的情况,所以,目前看来,Room配合RxJava,最大的好处就是不用写一堆线程切换的玩意儿了。

基本使用方法

返回的查询结果对象

INSERT,UPDATE,DELETE操作可以返回CompletableSingleMaybe三种对象,而QEURY操作,如果是一次性查询,可以返回SingleMaybe,如需要可观测对象,可以返回ObservableFlowable对象,但是注意,不能返回Completable对象。

这里有一点需要注意,是什么操作取决于具体做的是什么操作,而不是注解。听起来可能有点绕,举个栗子就明白了:@QUERY(DELETE ***)也属于DELETE操作。

先来简单介绍一下这几个属于RxJava的对象的特点。

  1. Completable:只有onCompleteonError方法,即是只有“完成”和“错误”两种状态,不会返回具体的结果。
  2. Single:其回调为onSuccessonError,查询成功会在onSuccess中返回结果,需要注意的是,如果未查询到结果,即查询结果为空,会直接走onError回调,抛出EmptyResultSetException异常。
  3. Maybe:其回调为onSuccessonErroronComplete,查询成功,如果有数据,会先回调onSuccess再回调onComplete,如果没有数据,则会直接回调onComplete
  4. Flowable/Observable:这俩相信不用多介绍了,这是返回一个可观察的对象,每当查询语句查询的部分有变化时,都会回调它的onNext方法,直到Rx流断开。

这次学习Room&RxJava操作倒是有一波意外之喜,认识了CompletableSingleMaybe三个新玩意儿。根据其特性来看,我个人更倾向于使用Single,原因有二:

  1. 我目前业务中进行的CRUD操作多为一次性操作,基本不需要持续观察数据库变化。
  2. 不管我查询有没有结果,总得给我个响啊,像Maybe这种,结果为空就直接连onSuccess都不回调了,这是万万不能接受的。

当然,关于Maybe这样的查询无结果也想要走onSuccess回调的话,还是有补救方法的,下文会提到。

CRUD基本操作

注意:以下分类是按照SQL操作类型分类,而不是按Room的注解来分类的。

这里我们使用User实体类来举例说明。

@Entity
data class User(
    @PrimaryKey
    val firstName: String,
    val lastName: String,
    val age: Int
)

INSERT

@Insert
fun insert(user: User): Completable

@Insert
fun insert(user: User): Single<Long>

@Insert
fun insert(user: User): Maybe<Long>

@Insert
fun insert(vararg users: User): Single<List<Long>>

@Insert
fun insert(vararg users: User): Maybe<List<Long>>

@Query("INSERT INTO user VALUES(:firstName, :lastName, :age)")
fun insert(firstName: String, lastName: String, age: Int): Single<Long>

@Query("INSERT INTO user VALUES(:firstName, :lastName, :age)")
fun insert(firstName: String, lastName: String, age: Int): Maybe<Long>

注意:

  1. 返回的类型是Long也只能是Long,否则无法通过编译。
  2. 返回的Long值,是指的插入的行id。

UPDATE/DELETE

@Update/Delete
fun insert(user: User): Completable

@Update/Delete
fun insert(user: User): Single<Int>

@Update/Delete
fun insert(user: User): Maybe<Int>

@Update/Delete
fun insert(vararg users: User): Single<List<Int>>

@Update/Delete
fun insert(vararg users: User): Maybe<List<Int>>

@Query("UPDATE user SET firstname = :firstName WHERE age = :age")
fun updateFirstName(age: Int, firstName: String): Single<List<Int>>

@Query("UPDATE user SET firstname = :firstName WHERE age = :age")
fun updateFirstName(age: Int, firstName: String): Maybe<List<Int>>

注意:

  1. 返回的类型为Integer也只能是Integer,否则无法通过编译。
  2. 返回的Integer值,指的是该次操作影响到的总行数,比如该次操作更新了5条,就返回5

QUERY

@Query("SELECT * FROM user")
fun query(): Single<List<User>>

@Query("SELECT * FROM user")
fun query(): Maybe<List<User>>

@Query("SELECT * FROM user")
fun query(): Flowable<List<User>>

@Query("SELECT * FROM user")
fun query(): Observable<List<User>>

@Query("SELECT * FROM user LIMIT 1")
fun query(): Single<User>

@Query("SELECT * FROM user LIMIT 1")
fun query(): Maybe<User>

@Query("SELECT * FROM user LIMIT 1")
fun query(): Flowable<User>

@Query("SELECT * FROM user LIMIT 1")
fun query(): Observable<User>

QUERY操作就没啥好说的了,简单明了,一看就懂。

总结

关于Maybe查询不到结果直接回调onComplete的问题

其实是有补救方法的,你可以操作单个对象的时候假装自己在操作一个列表,比如这样:

@Insert
fun insert(vararg users: User): Maybe<List<Long>>

或者虽然操作的是单个对象,但是强行叫它返回列表的结果,像这样:

@Insert
fun insert(user: User): Maybe<List<Long>>

经过这样一番操作后,如果查询到的结果为空,还是会回调onSuccess,会给你一个空列表[]
但是还是建议使用Single

关于Single的使用,可以用扩展函数来方便的进行线程切换

这里提供一个我自用的简单的切换线程的扩展函数:

/**
 * 数据库一次性查询结果扩展方法,目的是处理数据库查询返回的Single结果
 * 有结果调用onSuccess,空结果或查询出错一律调用onFailed
 */
fun <T> Single<T>.subscribeDbResult(onSuccess: (data: T) -> Unit, onFailed: (e: Throwable) -> Unit) {
    subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : SingleObserver<T> {
            override fun onSuccess(t: T) {
                onSuccess(t)
            }

            override fun onSubscribe(d: Disposable) {

            }

            override fun onError(e: Throwable) {
                onFailed(e)
            }
        })
}

进行查询的时候就可以这样调用了:

userDao.getAll()
    .subscribeDbResult({
        onSuccess(it)
    }, {
        onFailed(it)
    })

关于@Query返回Single和Maybe类型的Bug

目前改版本被我试出来一个bug,那就是用@Query的操作,在进行UPDATEDELETEINSERT操作时,直接通过RxJava进行线程切换是没用的,像下面这样写:

@Query("UPDATE user SET firstname = :firstName WHERE age = :age")
fun updateFirstName(age: Int, firstName: String): Single<List<Int>>

userDao.updateFirstName(18, "Xu")
        subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : SingleObserver<T> {
            override fun onSuccess(t: T) {
                onSuccess(t)
            }

            override fun onSubscribe(d: Disposable) {

            }

            override fun onError(e: Throwable) {
                onFailed(e)
            }
        })

这样会直接抛出java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.异常。
这就需要这样:

Single.just(Any())
    .flatMap {
        return@flatMap userDao.updateFirstName(18, "Xu")
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(object : SingleObserver<Int> {
        override fun onSuccess(t: Int) {
        }

        override fun onSubscribe(d: Disposable) {
        }

        override fun onError(e: Throwable) {
        }
    })

这个bug已经被Google注意到了,预计会在下一个alpha版本,即Room 2.1.0-alpha05中修复,详情见Defeered @Query with insert, update or delete throwing main thread exception.

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值