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

Fre*_*Cha 8 android kotlin android-livedata android-architecture-components android-workmanager

有人可以帮我找到我在哪里错了.我需要不断观察网络数据,并在工作人员发生数据更改时更新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"
    }

}
Run Code Online (Sandbox Code Playgroud)

其名称如下:

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()
}
Run Code Online (Sandbox Code Playgroud)

我还有一个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)
        }
    }}
Run Code Online (Sandbox Code Playgroud)

有人可以帮我找到我错的地方.这给我的错误如下:

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)
Run Code Online (Sandbox Code Playgroud)

use*_*087 20

改变这个:

networkData.observeForever { newData->
    appExecutors.diskIO().execute {
        userDao.insert(newData.user)
    }
}
Run Code Online (Sandbox Code Playgroud)

到:

变体 B(带有协程):

GlobalScope.launch(Dispatchers.Main) { networkData.observerForever { /*..*/ } }
Run Code Online (Sandbox Code Playgroud)

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

变体 A(无协程):

Handler(Looper.getMainLooper()).post { networkData.observeForever{ /*..*/ } }
Run Code Online (Sandbox Code Playgroud)

解释

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

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

  • 你是对的,还有一些其他的可能性可以切换到主线程。如果他使用 kotlin 协程,他也可以使用 `GlobalScope.launch(Dispatchers.Main) { }` 进行切换:) (4认同)

Erc*_*can 12

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

GlobalScope.launch(Dispatchers.Main) {
  // your code here...
}
Run Code Online (Sandbox Code Playgroud)