为什么房间删除操作(使用RxJava)即使指定了不同的订阅线程也会导致UI线程错误?

bla*_*ara 5 android rx-java2 android-room

简而言之,DAO

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

用法,

mDisposables.add(mThingsDao
    .deleteThings(listOfId)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
         ...
     }, {
         ...
     })
)
Run Code Online (Sandbox Code Playgroud)

和错误,

// Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Run Code Online (Sandbox Code Playgroud)

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

那我想念的是什么?


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

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>
Run Code Online (Sandbox Code Playgroud)

Epi*_*rce 7

有趣的问题远比看上去<3

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

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

生成的代码是:

  @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();
        }
      }
    });
  }
Run Code Online (Sandbox Code Playgroud)

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


看到这一行:

SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
Run Code Online (Sandbox Code Playgroud)

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

/**
 * 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);
}
Run Code Online (Sandbox Code Playgroud)

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



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

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

  • 您认为这是生成器中的错误吗?它为什么不将所有这些内容放入`fromCallable`的主体中?另外,如果我从多个线程同时运行生成的“也许”,它们是否会与“ _stmt.executeUpdateDelete()”发生冲突-打破了隔离冷序列的期望。也许值得向Room开发人员报告此错误。顺便说一句,不需要那种复杂的flatMapping。只需使用Maybe.defer(()-&gt; mThingsDao .deleteThings(listOfId))。subscribeOn(Schedulers.io())...` (2认同)

yig*_*git 5

这是一个错误,已经修复,将与一起发布 2.1.0-alpha05