为什么房间删除操作(使用RxJava)即使指定了不同的订阅线程也会导致UI线程错误? [英] Why Does Room Delete Operation(With RxJava) Gives UI Thread Error Even Specifying Different Subcribe Thread?

查看:54
本文介绍了为什么房间删除操作(使用RxJava)即使指定了不同的订阅线程也会导致UI线程错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很简单,DAO

@Query("DELETE FROM Things WHERE someIdOfTheThing IN (:listOfId)")
abstract fun deleteThings(listOfId: MutableList<String>): Maybe<Int>

用法

mDisposables.add(mThingsDao
    .deleteThings(listOfId)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
         ...
     }, {
         ...
     })
)

和错误

// Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

我当时想到的简单想法是指定subscribeOn(Schedulers.io()),然后将所有工作交给Rx的魔术师,但失败了.

The simple idea i was thinking is to specify subscribeOn(Schedulers.io()) and then give all the job to Rx's magical hands, but failed.

那我想念的是什么?

像下面这样包装并使用deleteThingsWrapped后,开始工作.但是仍然不明白为什么第一种方法不起作用

After wrapping like below and using deleteThingsWrapped, started working. But still don't understand why first approach not worked

open fun deleteThingsWrapped(listOfId: MutableList<String>): Maybe<Int> {
    return Maybe.create(object : MaybeOnSubscribe<Int> {
        override fun subscribe(emitter: MaybeEmitter<Int>) {
            emitter.onSuccess(deleteThings(listOfId))
        }
    })
}

@Query("DELETE FROM Things WHERE someIdOfTheThing IN (:listOfId)")
abstract fun deleteThings(listOfId: MutableList<String>): Maybe<Int>

推荐答案

比看来< 3

要解决您的问题,我们必须查看Room生成的代码,内容如下:

To solve your problem, we must look at the code generated by Room - for the following:

@Transaction
@Query("DELETE FROM plants WHERE id IN (:listOfId)")
abstract fun deleteThings(listOfId: MutableList<String>): Maybe<Int>

生成的代码是:

  @Override
  public Maybe<Integer> deleteThings(final List<String> listOfId) {
    StringBuilder _stringBuilder = StringUtil.newStringBuilder();
    _stringBuilder.append("DELETE FROM plants WHERE id IN (");
    final int _inputSize = listOfId.size();
    StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
    _stringBuilder.append(")");
    final String _sql = _stringBuilder.toString();
    SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
    int _argIndex = 1;
    for (String _item : listOfId) {
      if (_item == null) {
        _stmt.bindNull(_argIndex);
      } else {
        _stmt.bindString(_argIndex, _item);
      }
      _argIndex ++;
    }
    return Maybe.fromCallable(new Callable<Integer>() {
      @Override
      public Integer call() throws Exception {
        __db.beginTransaction();
        try {
          final java.lang.Integer _result = _stmt.executeUpdateDelete();
          __db.setTransactionSuccessful();
          return _result;
        } finally {
          __db.endTransaction();
        }
      }
    });
  }

因此,我们看到该操作本身在Maybe.fromCallable块内,这是受subscribeOn(Schedulers.io())影响的部分.因此,如果executeUpdateDelete();是在后台线程上执行的,为什么会出现异常?

So we see that the operation itself IS inside a Maybe.fromCallable block, which is the part that will be affected by subscribeOn(Schedulers.io()). So if the executeUpdateDelete(); is executed on the background thread, why do you get an exception?

查看此行:

SupportSQLiteStatement _stmt = __db.compileStatement(_sql);

如果我们检查此方法的内部内容......

If we check the inside of this method....

/**
 * Wrapper for {@link SupportSQLiteDatabase#compileStatement(String)}.
 *
 * @param sql The query to compile.
 * @return The compiled query.
 */
public SupportSQLiteStatement compileStatement(@NonNull String sql) {
    assertNotMainThread(); // <-- BOOM!
    return mOpenHelper.getWritableDatabase().compileStatement(sql);
}

因此,显然即使将查询放在一起也被断言不在主线程上,因此具有Maybe是无关紧要的.无论您做什么,Maybe.fromCallable之外的块都会在当前线程上执行.

So apparently even putting together the query is asserted to not be on the main thread, therefore having a Maybe is irrelevant; the block outside of the Maybe.fromCallable is executed on the current thread no matter what you do.

您也可以通过执行同步执行部分"以在后台线程上执行来解决此问题.

You can solve this by executing the "synchronously executed part" to execute on a background thread, too.

mDisposables.add(
    Maybe.defer { 
        mThingsDao.deleteThings(listOfId)
                         .subscribeOn(Schedulers.io())
    }.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
       ...
    }, {
       ...
    })
)

这篇关于为什么房间删除操作(使用RxJava)即使指定了不同的订阅线程也会导致UI线程错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆