异步更新视图

Jos*_*oes 7 android thread-exceptions kotlin kotlin-coroutines

我正在尝试recyclerview使用我要异步获取的来自网络的数据填充。

我有一个函数loadData(),该函数onCreateView()首先使加载指示器可见,然后调用suspend函数加载数据,然后尝试通知视图适配器进行更新。

但是在这一点上,我得到以下异常:

android.view.ViewRootImpl $ CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触摸其视图。

令我感到惊讶的是,我的理解是只有get_top_books()在不同的线程上调用了我的函数,而以前在显示加载指示器时,我显然是在正确的线程上。

那么,为什么会引发此运行时异常?

我的代码:

class DiscoverFragment: Fragment() {

    lateinit var loadingIndicator: TextView
    lateinit var viewAdapter: ViewAdapter
    var books = Books(arrayOf<String>("no books"), arrayOf<String>("no books"), arrayOf<String>("no books"))

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val viewFrame = layoutInflater?.inflate(R.layout.fragment_discover, container, false)

        val viewManager = GridLayoutManager(viewFrame!!.context, 2)
        viewAdapter = ViewAdapter(books)
        loadingIndicator = viewFrame.findViewById<TextView>(R.id.loading_indicator)

        val pxSpacing = (viewFrame.context.resources.displayMetrics.density * 8f + .5f).toInt()

        val recyclerView = viewFrame.findViewById<RecyclerView>(R.id.recycler).apply {
            setHasFixedSize(true)
            layoutManager = viewManager
            adapter = viewAdapter
            addItemDecoration(RecyclerViewDecorationSpacer(pxSpacing, 2))
        }

        loadData()

        return viewFrame
    }

    fun loadData() = CoroutineScope(Dispatchers.Default).launch {
        loadingIndicator.visibility = View.VISIBLE
        val task = async(Dispatchers.IO) {
            get_top_books()
        }
        books = task.await()
        viewAdapter.notifyDataSetChanged()
        loadingIndicator.visibility = View.INVISIBLE
    }
}
Run Code Online (Sandbox Code Playgroud)

And*_*ilo 12

调用后,books = task.await()您在 UI 线程之外。那是因为你使用CoroutineScope(Dispatchers.Default). 将其更改为Dispatchers.Main

fun loadData() = CoroutineScope(Dispatchers.Main).launch {
        loadingIndicator.visibility = View.VISIBLE
        val task = async(Dispatchers.IO) {
            get_top_books()
        }
        books = task.await()
        viewAdapter.notifyDataSetChanged()
        loadingIndicator.visibility = View.INVISIBLE
    }
Run Code Online (Sandbox Code Playgroud)