java.lang.IllegalStateException:无法在后台线程上调用 observeForever



有人能帮我找到我在这里出错的地方吗?我需要持续观察网络数据,并在工作线程的数据发生更改时更新 UI。请注意,这在升级到 androidx 之前是有效的。

下面是一个辅助角色类。

class TestWorker(val context: Context, val params: WorkerParameters): Worker(context, params){
override fun doWork(): Result {
Log.d(TAG, "doWork called")
val networkDataSource = Injector.provideNetworkDataSource(context)
networkDataSource.fetchData(false)
return Worker.Result.SUCCESS
}
companion object {
private const val TAG = "MY_WORKER"
}
}

其调用如下:

fun scheduleRecurringFetchDataSync() {
Log.d("FETCH_SCHEDULER", "Scheduling started")
val fetchWork = PeriodicWorkRequest.Builder(TestWorker::class.java, 1, TimeUnit.MINUTES)
.setConstraints(constraints())
.build()
WorkManager.getInstance().enqueue(fetchWork)
}
private fun constraints(): Constraints{
return Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
}

我还有一个UserDao和UserRepository来获取和存储数据。我正在观察用户存储库中的网络数据,如下所示:

class UserRepository (
private val userDao: UserDao,
private val networkDataSource: NetworkDataSource,
private val appExecutors: AppExecutors){
init {
val networkData= networkDataSource.downloadedData
networkData.observeForever { newData->
appExecutors.diskIO().execute {
userDao.insert(newData.user)
}
}}

有人可以帮助我找到我出错的地方吗?这给了我错误如下:

java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:443)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:204)
at com.example.app.data.repo.UserRepository.<init>(UserRepository.kt:17)
at com.example.app.data.repo.UserRepository$Companion.getInstance(UserRepository.kt:79)

更改以下内容:

networkData.observeForever { newData->
appExecutors.diskIO().execute {
userDao.insert(newData.user)
}
}

自:

变体 B(带协程(:

GlobalScope.launch(Dispatchers.Main) { networkData.observerForever { /*..*/ } }

但请注意,不建议使用GlobalScope:https://stackoverflow.com/a/54351785/1185087

变体 A(不带协程(:

Handler(Looper.getMainLooper()).post { networkData.observeForever{ /*..*/ } }

解释

通常observe(..)observeForever(..)应该从主线程调用,因为它们的回调(Observer<T>.onChanged(T t)(经常改变UI,这只能在主线程中实现。这就是为什么 android 检查观察函数的调用是否由主线程完成的原因。

在您的情况下,UserRepository.init{}由后台线程调用,因此会引发异常。要切换回主线程,您可以使用上述变体之一。但请注意,观察回调中的代码也由主线程执行。此回调中的任何昂贵处理都将冻结您的 UI!

在另一种解决方案中,您可以从主调度程序调用它为

GlobalScope.launch(Dispatchers.Main) {
// your code here...
}

就我而言,我正在测试liveData,但我忘记添加InstantTaskExecutorRule((。

@RunWith(AndroidJUnit4::class)
class UserDaoTest {
@get:Rule // <----
var instantExecutorRule = InstantTaskExecutorRule() // <----
....
}

不要忘记将库添加到项目中。

testImplementation"androidx.arch.core:core-testing:2.1.0" // unit tests
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"//instrumentation tests

除了来自@user1185087的漂亮而详细的答案之外,如果您在项目中使用RxJava,这里还有一个解决方案。它可能不是那么短,但如果你已经在项目中使用了 RxJava,这是一种切换到所需线程的优雅方式(在这种情况下,通过.observeOn(AndroidSchedulers.mainThread())切换到 Android 的 UI 线程(。

Observable.just(workManager.getStatusById(workRequest.getId()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(status -> status.observeForever(workStatus -> {
// Handling result on UI thread
}), err -> Log.e(TAG, err.getMessage(), err));
  • 这是我在 Java 代码中所做的工作,以使其工作
// the LiveData query
LiveData<List<Calf>> calfLiveDataList = getCalfDao().getAllCalves();
Handler handler = new Handler(Looper.getMainLooper()); //This is the main thread
handler.post(new Runnable() { //task to run on main thread
@Override
public void run() {
calfLiveDataList.observeForever(observer);
}
}
);
  • 忽略我的命名约定,我的应用程序适用于奶农。

最新更新