调试RxJava代码时触发CalledFromWrongThreadException问题求助
问题原因分析
调试时出现CalledFromWrongThreadException,但正常运行时没问题,核心原因是断点打乱了RxJava的线程调度顺序,让原本被线程调度巧合掩盖的问题暴露了:你的流中部分DetailUiModel(比如带有城市信息的loading状态)没有被正确切换到主线程发射,调试时的线程暂停让这部分逻辑落到了非主线程执行,触发了Android的UI线程检查机制。
解决方案
方案一:修正RxJava流的线程调度顺序
把startWith移到observeOn(AndroidSchedulers.mainThread())之前,确保所有发射的DetailUiModel都会经过主线程调度后再传递给handleResponse:
private val observeRefresh = detailView.observeRefresh .observeOn(Schedulers.io()) .map { _ -> detailModel.forecastCity } .flatMap { city -> detailModel.getForecast(city.id, FORECAST_COUNT) .map { response -> DetailUiModel.stateSuccess(response.list, detailModel.forecastCity) } .onErrorReturn { t -> DetailUiModel.stateError(t.message) } .startWith(DetailUiModel.stateLoading) // 先添加loading状态,再统一切换到主线程 .observeOn(AndroidSchedulers.mainThread()) } .subscribe { this.handleResponse(it) }
这样不管是loading状态还是请求结果,都会被强制切换到主线程,从根源上避免UI操作在非主线程执行。
方案二:强制在主线程执行UI操作
如果担心后续流的线程调度再出问题,可以直接在handleResponse中用runOnUiThread包裹所有UI操作逻辑,确保无论handleResponse被哪个线程调用,UI操作都在主线程执行:
private fun handleResponse(model: DetailUiModel) { // 用runOnUiThread强制所有UI操作在主线程执行 (context as? AppCompatActivity)?.runOnUiThread { detailView.setLoading(model.isLoading) when { model.success -> detailView.setForecastItems(model.data!!) !model.isLoading -> detailView.showError(model.error) model.city != null -> detailView.setMapImage(createMapUrl(model.city!!)) else -> return@runOnUiThread } } }
这个方案更保险,相当于给UI操作加了一层线程安全防护。
内容的提问来源于stack exchange,提问作者Axbor Axrorov
相关产品推荐
相关产品推荐

