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>
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({
...
}, {
...
})
)
This is a bug, is already fixed and will be released with 2.1.0-alpha05
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With