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

问题描述 投票:6回答:2

有人可以帮我找到我在哪里错了。我需要不断观察网络数据,并在工作人员发生数据更改时更新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来获取和存储数据。我正在观察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)
android kotlin android-architecture-components android-workmanager android-livedata
2个回答
3
投票

改变这个:

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

对此:

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

说明

通常observe(..)observeForever(..)应该从主线程中调用,因为它们的回调(Observer<T>.onChanged(T t))经常改变UI,这只能在主线程中实现。这就是为什么android检查观察函数的调用是否由主线程完成的原因。如果没有抛出异常(IllegalStateException: Cannot invoke observeForever on a background thread)。在你的情况下,UserRepository.init{}由后台线程调用,因此抛出异常。要切换回主线程,您可以通过调用RunnableHandler(Looper.getMainLooper()).post {}发布到主线程。但请注意,您的observe回调中的代码也是由主线程执行的。此回调中的任何昂贵处理都将冻结您的UI!


0
投票

除了来自@ 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));
© www.soinside.com 2019 - 2024. All rights reserved.