Slazer
Slazer

Reputation: 4990

Iterator value undefined on first run

In my onViewCreated() inside my MythFragment I do these steps.

  1. Prepopulate Room database.
  2. Fetch all records from database into MutableLiveData<List<..>>
  3. Initialize an iterator to these data
  4. Click the "Next" button an check condition it.hasNext() == true

For some reason, only during the first run of the program, the it.hasNext() gives me false. I would expect it to be true, since the steps 1-3 should've already ensured, that the list is filled and the iterator points to the first element.

Interestingly, any later navigation on the MythView retrieves the element correctly and it.hasNext() gives me true.

MythFragment.kt

class MythFragment : Fragment() {

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

        viewModel = ViewModelProviders.of(this).get(MythViewModel::class.java)
        viewModel.populateMyths()
        viewModel.fetchFromDatabase()

        buttonNextMyth.setOnClickListener {
            mythEvaluation.visibility = View.GONE
            buttonIsThisMythTruthful.visibility = View.VISIBLE

            viewModel.mythsIterator.let {
                if (it.hasNext()) {
                    val myth = it.next()
                    mythText.text = myth.myth
                    mythEvaluation.text = myth.evaluation
                } else {
                    Toast.makeText(activity, "There is no myth, because it.hasNext() is false!)", Toast.LENGTH_SHORT).show()
                    val action = MythFragmentDirections.actionMenuFragment()
                    Navigation.findNavController(view).navigate(action)
                }
            }
        }
    }
}

MythViewModel.kt

class MythViewModel(application: Application) : BaseViewModel(application) {

    private val myths = MutableLiveData<List<Myth>>()
    lateinit var mythsIterator: Iterator<Myth>

    fun populateMyths() {
        launch {
            val dao = MythDatabase(getApplication()).mythDao()

            if (dao.getRowCount() > 0)
                return@launch

            val mythList = arrayListOf(
                Myth("This is the myth 1", "This is the evaluation of the myth 1"),
                Myth("This is the myth 2", "This is the evaluation of the myth 2"),
                Myth("This is the myth 3", "This is the evaluation of the myth 3"),
                Myth("This is the myth 4", "This is the evaluation of the myth 4"),
                Myth("This is the myth 5", "This is the evaluation of the myth 5")
            )

            dao.insertAll(
                *mythList.toTypedArray()
            )
        }
    }

    fun fetchFromDatabase() {
        launch {
            val mythList = MythDatabase(getApplication()).mythDao().getAllMyths()
            myths.value = mythList
            myths.value?.let {
                mythsIterator = it.iterator()
            }
        }
    }
}

I believe the problem might be in the concurrency (Coroutines), but I don't understand what am I doing wrong.

Upvotes: 1

Views: 143

Answers (1)

herman
herman

Reputation: 12305

As both functions populateMyths() and fetchFromDatabase() are launching new coroutines, both these coroutines will run in parallel. So the first time fetchFromDatabase() is called, it may be retrieving the data before dao.insertAll() has happened in populateMyths(). Maybe you should rethink / clarify what you aim to accomplish by launching these coroutines.

Upvotes: 2

Related Questions