Tengo que actualizar la interfaz de usuario con una llamada asíncrona a la base de datos de habitaciones, pero cuando lo hago, aparece este error: android.view.ViewRootImpl$CalledFromWrongThreadException: solo el hilo original que creó una jerarquía de vistas puede tocar sus vistas.
// FavoritosPresentador.kt
GlobalScope.launch { favoritesView.showFavorites(ProductProvider.getAllProducts() as ArrayList<Product>) }// ProveedorProducto.kt
fun getAllProducts() : MutableList<Product> { return dao.getAllProducts() }// ProductDao.kt
@Query("SELECT * FROM product") fun getAllProducts(): MutableList<Product>Lo que necesito es actualizar mi interfaz de usuario a través de mi ProductProvider, ya que lo usaré para todas mis entidades. Necesito una solución confiable.
Debe buscar desde la sala mediante una rutina de E/S y cambiar a una rutina principal (IU) para actualizar la vista.
Tratar:
GlobalScope.launch(Dispatchers.IO) { val products = ProductProvider.getAllProducts() as ArrayList<Product> withContext(Dispatchers.Main) { favoritesView.showFavorites(products) } }Asegúrese de tener instalada la biblioteca Coroutine de Android para que Main Dispatcher reconozca correctamente el subproceso principal de Android.
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"Room 2.1 (actualmente en alfa) agrega soporte para corrutinas de Kotlin . Puedes hacer lo siguiente:
Marque las funciones en ProductDao y ProductProvider como suspendidas :
// ProductDao.kt @Query("SELECT * FROM product") suspend fun getAllProducts(): List<Product> // ProductProvider.kt suspend fun getAllProducts(): List<Product> = dao.getAllProducts() Cree un alcance local para una rutina en FavoritesPresenter :
class FavoritesPresenter { private var favoritesView: FavoritesView? = null private val provider: ProductProvider = ...// initialize it somehow private var job: Job = Job() private val scope = CoroutineScope(job + Dispatchers.Main) fun getProducts() { scope.launch { favoritesView?.showFavorites(provider.getAllProducts()) } } fun attachView(view: FavoritesView) { this.favoritesView = view } fun detachView() { job.cancel() // cancel the job when Activity or Fragment is destroyed favoritesView = null } interface FavoritesView { fun showFavorites(products: List<Product>) } } Use FavoritesPresenter en Activity o Fragment :
class MainActivity : AppCompatActivity(), FavoritesPresenter.FavoritesView { lateinit var presenter: FavoritesPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... presenter = FavoritesPresenter() presenter.attachView(this) presenter.getProducts() } override fun onDestroy() { presenter.detachView() super.onDestroy() } override fun showFavorites(products: List<Product>) { // use products to update UI } }Para utilizar la importación de Dispatchers.Main :
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'Sería mejor no usar GlobalScope, sino usar su propio CoroutineContext, por ejemplo:
class YourActivity : CoroutineScope { private lateinit var job: Job // context for io thread override val coroutineContext: CoroutineContext get() = Dispatchers.IO + job override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) job = Job() } fun toDoSmth() { launch { // task, do smth in io thread withContext(Dispatchers.Main) { // do smth in main thread after task is finished } } } override fun onDestroy() { job.cancel() super.onDestroy() } }