【问题标题】:Update UI async call with coroutines使用协程更新 UI 异步调用
【发布时间】:2019-07-07 09:11:15
【问题描述】:

我必须使用对房间数据库的异步调用来更新 UI,但是当我这样做时,我遇到了这个错误:android.view.ViewRootImpl$CalledFromWrongThreadException: 只有创建视图层次结构的原始线程可以触摸其观点。

//FavoritesPresenter.kt

GlobalScope.launch {
    favoritesView.showFavorites(ProductProvider.getAllProducts() as ArrayList<Product>)
}

// ProductProvider.kt

fun getAllProducts() : MutableList<Product> {
    return dao.getAllProducts()
}

// ProductDao.kt

@Query("SELECT * FROM product")
fun getAllProducts(): MutableList<Product>

我需要通过我的 ProductProvider 更新我的 UI,因为我将用于我需要可靠解决方案的所有实体。

【问题讨论】:

    标签: android kotlin android-room kotlin-coroutines coroutine


    【解决方案1】:

    您应该使用 IO 协程从 Room 中获取数据,然后切换到主 (UI) 协程以更新视图。

    试试:

    GlobalScope.launch(Dispatchers.IO) {
                val products = ProductProvider.getAllProducts() as ArrayList<Product>
                withContext(Dispatchers.Main) {
                    favoritesView.showFavorites(products)
                }
            }
    

    确保安装了 Android Coroutine 库,以便 Main Dispatcher 正确识别 Android 主线程。

    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
    

    【讨论】:

      【解决方案2】:

      最好不要使用GlobalScope,而是使用自己的CoroutineContext,例如:

      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()
         }
      }
      

      【讨论】:

      • 使用 GlobalScope 并创建您自己的上下文在性能或速度上是否存在差异?
      • 对于 Android 组件,您可以完成所有 IO 工作,只需取消作业。它可以防止内存泄漏。查看更新的答案。
      【解决方案3】:

      Room 2.1(目前处于 alpha 阶段)增加了对 Kotlin 协程的支持。您可以执行以下操作:

      1. ProductDaoProductProvider 中的函数标记为suspend

        // ProductDao.kt
        @Query("SELECT * FROM product")
        suspend fun getAllProducts(): List<Product>
        
        // ProductProvider.kt
        suspend fun getAllProducts(): List<Product> = dao.getAllProducts()
        
      2. 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>)
            }
        }
        
      3. ActivityFragment 中使用FavoritesPresenter

        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
            }
        }
        

      要使用 Dispatchers.Main 导入:

      implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
      

      【讨论】:

        猜你喜欢
        • 2014-02-16
        • 1970-01-01
        • 2010-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多