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

后端 未结 2 2045
隐瞒了意图╮
隐瞒了意图╮ 2021-01-05 14:26

So simply, the DAO

@Query(\"DELETE FROM Things WHERE someIdOfTheThing IN (:listOfId)\")
abstract fun deleteThings(listOfId: MutableList): Maybe         


        
相关标签:
2条回答
  • 2021-01-05 14:43

    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({
           ...
        }, {
           ...
        })
    )
    
    0 讨论(0)
  • 2021-01-05 14:56

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

    0 讨论(0)
提交回复
热议问题