Meltix
Meltix

Reputation: 499

Android: Coroutine doesn't always work on call

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 happens What should happen

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

Answers (1)

Onik
Onik

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

Related Questions