我必须通过异步调用Room Database来更新UI,但是当我收到此错误时:android.view.ViewRootImpl $ CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触摸它的观点。
//收藏夹Presenter.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,因为我将对所有实体使用可靠的解决方案。
答案 0 :(得分:3)
您应该使用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 Main Thread。
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
答案 1 :(得分:3)
2.1室(当前为Alpha版)增加了对 Kotlin协程的支持。您可以执行以下操作:
将ProductDao
和ProductProvider
中的功能标记为暂停:
// ProductDao.kt
@Query("SELECT * FROM product")
suspend fun getAllProducts(): List<Product>
// ProductProvider.kt
suspend fun getAllProducts(): List<Product> = dao.getAllProducts()
在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>)
}
}
在FavoritesPresenter
或Activity
中使用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
}
}
要使用 Dispatchers.Main 导入:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
答案 2 :(得分: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()
}
}