Android:协程并不总是随叫随到
Android: Coroutine doesn't always work on call
我正在开发一个应用程序,它在 Fragment 中有一个 RecyclerView。 Fragment的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())
检查应该可以解决问题。
我正在开发一个应用程序,它在 Fragment 中有一个 RecyclerView。 Fragment的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())
检查应该可以解决问题。