Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Does Room Delete Operation(With RxJava) Gives UI Thread Error Even Specifying Different Subcribe Thread?

So simply, the DAO

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

usage,

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

and error,

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

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.

So what's the thing i'm missing ?


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>
like image 682
blackkara Avatar asked Feb 17 '19 00:02

blackkara


2 Answers

Much more interesting question than it seems <3

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>

The generated code is:

  @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();
        }
      }
    });
  }

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?


See this line:

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);
}

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({
       ...
    }, {
       ...
    })
)
like image 148
EpicPandaForce Avatar answered Sep 28 '22 01:09

EpicPandaForce


This is a bug, is already fixed and will be released with 2.1.0-alpha05

like image 44
yigit Avatar answered Sep 28 '22 00:09

yigit