有人能帮我找到我在这里出错的地方吗?我需要持续观察网络数据,并在工作线程的数据发生更改时更新 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);
}
}
);
- 忽略我的命名约定,我的应用程序适用于奶农。