Android:协程并不总是随叫随到

Android: Coroutine doesn't always work on call

我正在开发一个应用程序,它在 Fragment 中有一个 RecyclerViewFragment的objective是用游标取Tracks的数据。由于这个过程很长,我决定实现一个 ViewModel,使用协程让进程运行得更快。

但是,我对此有一个问题:协程并不总是按预期启动。出于某种原因,当我打开应用程序时,有时协程会在 Fragment 打开后立即执行,然后在 RecyclerView 中显示正确的数据,有时它不会。

会发生什么(左)和应该发生什么(右):

这是怎么回事?有办法解决这个问题吗?

Fragment.kt

class FragmentTrack : Fragment(), AdapterOneColumn.OnItemClickListener {
    var trackList = mutableListOf<DataItems>()

    ....

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ....

        if(trackList.isEmpty()) {
            val cttResolver : ContentResolver? = activity?.contentResolver
            ViewModelCycleTracks().recyclerNeedsTracks(trackList, cttResolver)
        }

        rvTracks.apply {
            setHasFixedSize(true)
            layoutManager = LinearLayoutManager(activity)
            adapter = AdapterOneColumn(trackList, this@FragmentTrack)
        }
    }

    override fun OnItemClick(position: Int) {
        Toast.makeText(context, "Pressed song", Toast.LENGTH_SHORT).show()
    }

}

ViewModel.kt

class ViewModelCycleTracks : ViewModel() {
    fun recyclerNeedsTracks(trackList: MutableList<DataItems>, cttResolver: ContentResolver?) {
        viewModelScope.launch {
            cycleFiles(trackList, cttResolver)
        }
    }

    suspend fun cycleFiles(trackList: MutableList<DataItems>, cttResolver: ContentResolver?) {
        return GlobalScope.async(Dispatchers.IO) {

            var songCursor : Unit? = cttResolver?.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            null, null, null, null)?.use {cursor ->
                val idSongName = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)
                val idSongArtist = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
                val idSongArt = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)

                while (cursor.moveToNext()) {
                    val songName = cursor.getString(idSongName)
                    val songArtist = cursor.getString(idSongArtist)
                    val songArt = cursor.getString(idSongArt)

                    trackList.add(DataItems(songName, songArtist))
                }

            }
        }.await()
    }
}

根据代码,协程不会在条件 if(trackList.isEmpty())false 的情况下启动。如果 FragmentTrack 的视图表示在导航到另一个 片段 时被破坏,而 FragmentTrack 实例保持活动状态且 trackList 为 non-empty第1个运行之后。当您导航回 FragmentTrack 时,trackList 不为空,因此协程不会启动,导致“空”RecyclerView

删除 if(trackList.isEmpty()) 检查应该可以解决问题。