Quelqu'un peut-il m'aider à trouver où je me trompe ici. J'ai besoin d'observer continuellement les données du réseau et de mettre à jour l'interface utilisateur chaque fois qu'il y a un changement de données du travailleur. Veuillez noter que cela fonctionnait avant la mise à niveau vers androidx.
Voici une classe ouvrière.
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"
}
}
Qui s'appelle comme suit:
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()
}
J'ai également un UserDao et UserRepository pour récupérer et stocker des données. J'observe les données réseau dans UserRepository comme suit:
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)
}
}}
Quelqu'un peut-il m'aider à localiser où je me trompe? Cela me donne une erreur comme suit:
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)
Change ça:
networkData.observeForever { newData->
appExecutors.diskIO().execute {
userDao.insert(newData.user)
}
}
Pour ça:
Handler(Looper.getMainLooper()).post {
networkData.observeForever { newData->
appExecutors.diskIO().execute {
userDao.insert(newData.user)
}
}
}
Normalement, observe(..)
et observeForever(..)
doivent être appelés à partir du thread principal car leurs rappels (Observer<T>.onChanged(T t)
) changent souvent l'interface utilisateur, ce qui n'est possible que dans le thread principal. C'est la raison pour laquelle Android vérifie si l'appel des fonctions d'observation est effectué par le thread principal. Sinon une exception est levée (IllegalStateException: Cannot invoke observeForever on a background thread
). Dans votre cas UserRepository.init{}
Est appelé par un thread d'arrière-plan, donc l'exception est levée. Pour revenir au thread principal, vous pouvez publier un Runnable
au thread principal en appelant Handler(Looper.getMainLooper()).post {}
. Mais attention le code à l'intérieur de votre rappel observe est également exécuté par le thread principal. Tout traitement coûteux à l'intérieur de ce rappel gèlera votre interface utilisateur!
Dans une autre solution, vous pouvez l'appeler depuis le répartiteur principal en tant que
GlobalScope.launch(Dispatchers.Main) {
// your code here...
}
En plus de la réponse agréable et détaillée de @ user1185087, voici une solution si vous utilisez RxJava dans votre projet. Ce n'est peut-être pas si court, mais si vous utilisez déjà RxJava dans votre projet, c'est un moyen élégant de basculer vers le thread requis (dans ce cas, le thread d'interface utilisateur d'Android via .observeOn(AndroidSchedulers.mainThread())
).
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));