Reputation: 499
I am working on an app which has a RecyclerView inside a Fragment. The objective of the Fragment, is to fetch with a cursor Tracks's data. Since this process is long, I decide to implement a ViewModel, using a coroutine to let the processes work faster.
However, I have a problem with this: the coroutine doesn't always start as it should. For some reason, when I open the app, sometimes the coroutine executes as soon as the Fragment gets opened, showing then the proper data inside the RecyclerView, and sometimes it doesn't.
What happens (left) and what should happen (right):
What is happening? Is there a way to fix this?
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()
}
}
Upvotes: 2
Views: 744
Reputation: 19959
According to the code, the coroutine wouldn't start in case the condition if(trackList.isEmpty())
is false
. This happens if the view representation of FragmentTrack
gets destroyed when navigating to another Fragment, while the FragmentTrack
instance remains alive with the trackList
being non-empty after the 1st run. When you navigate back to FragmentTrack
, trackList
isn't empty, so the coroutine won't launch resulting in an "empty" RecyclerView
.
Removing the if(trackList.isEmpty())
check should fix it.
Upvotes: 1