android - 为什么房间删除操作(使用 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 的神奇之手,但失败了。
那么我错过了什么?
像下面这样包装并使用后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>
解决方案
比看起来更有趣的问题<3
为了解决您的问题,我们必须查看 Room 生成的代码 - 用于以下内容:
@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();
后台线程上执行,为什么会出现异常呢?
看到这一行:
SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
如果我们检查这个方法的内部......
/** * 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); }
因此,显然即使将查询放在一起,也断言不在主线程上,因此具有 aMaybe
是无关紧要的;Maybe.fromCallable
不管你做什么,外面的块都会在当前线程上执行。
您也可以通过在后台线程上执行“同步执行部分”来解决此问题。
mDisposables.add(
Maybe.defer {
mThingsDao.deleteThings(listOfId)
.subscribeOn(Schedulers.io())
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
...
}, {
...
})
)
推荐阅读
- reactjs - 如何在一个主机上部署 Strapi 和 React?
- mysql - 具有相同 where 子句的 SELECT 和 UPDATE 查询得到不同的行计数结果?
- ios - 是否有使用 UIWebView API 的 cordova 插件列表?
- metadata - IPTCInfo 更改关键字不带引号
- javascript - NestJS/swagger:ApiExtraModel 期望什么模型作为参数?
- javascript - 如何使用 js.erb 响应 fetch(post)?
- azure-logic-apps - 逻辑应用程序 - 重复计划程序
- c++ - 分而治之的冒泡排序算法
- asp.net - 401 未经授权响应 CORS 预检选项请求
- ruby - 如何呈现富文本内容