【问题标题】:Iterator value undefined on first run首次运行时未定义迭代器值
【发布时间】:2020-11-02 01:58:59
【问题描述】:

在我的onViewCreated() 里面我的MythFragment 我执行这些步骤。

  1. 预填充 Room 数据库。
  2. 从数据库中获取所有记录到MutableLiveData<List<..>>
  3. 初始化这些数据的迭代器
  4. 单击“下一步”按钮检查条件it.hasNext() == true

由于某种原因,仅在程序的第一次运行期间,it.hasNext() 给了我false。我希望这是真的,因为步骤 1-3 应该已经确保列表已填充并且迭代器指向第一个元素。

有趣的是,MythView 上的任何后续导航都会正确检索元素,it.hasNext() 给我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()
            }
        }
    }
}

我认为问题可能出在并发(协程)上,但我不明白我做错了什么。

【问题讨论】:

  • 我还不知道是什么原因造成的,但是您使用?.let 而不是if (mythList != null) { mythsIterator = mythList.iterator(); } 有什么特别的原因吗?您是否尝试过在数据库查找后在行上设置断点,或者记录列表中的项目数?
  • 顺便说一句,您可能甚至不需要使用lateinit var,而只需使用计算属性:val mythsIterator get() = myths.value.iterator()。每次调用时都会调用 getter 并返回一个新的迭代器,这可能会更好,因为否则你只能迭代一次。
  • @herman 我只是按照教程中的做法。此外,我认为?.let 有一些优势,因为它适用于“变异属性”(discuss.kotlinlang.org/t/let-vs-if-not-null/3542)。我不确定那是什么,但安全总比抱歉好。我对调试不是很熟悉,我认为这太难了。我可以试试看。
  • 如果你检查 mythList 是否为空,那是 100% 安全的,因为它是 1) 一个 val,所以不能被变异,2) 本地,所以即使它是一个 var,它也不能t 在此函数之外发生变异。如果不能保证 null 安全性(例如,如果使用可以从该函数外部改变的字段),那么 Kotlin 不会让代码编译(不添加 !!)。在任何情况下,将单个迭代器保留为只能迭代一次的属性似乎并不正确。

标签: kotlin concurrency android-room kotlin-coroutines


【解决方案1】:

由于populateMyths()fetchFromDatabase() 两个函数都在启动新的协程,这两个协程将并行运行。所以第一次调用fetchFromDatabase(),它可能是在dao.insertAll() 发生在populateMyths() 之前检索数据。 也许您应该重新考虑/澄清通过启动这些协程来实现的目标。

【讨论】:

  • 我需要在应用程序启动后立即在populateMyths() 中预填充我的数据库。因此,将此函数放入MythFragment.onViewCreated() 可能不是一个好主意。我必须确保在从MythFragment 调用fetchFromDatabase() 之前预填充数据库。为了实现这一点,我尝试将populateMyths() 移动到onCreate(),但它仍然没有足够快地填充。我应该从 MainActivity 还是 MainFragment 调用它?这是一个好的设计决策吗?
  • @Slazer 初始化数据库的最佳位置是什么,这是另一个问题。 (不能帮你)。但问题是,您似乎想按顺序运行这两种方法。不要在每个方法中都放置一个launch 块,而是尝试删除它们并在对这两个方法的调用周围添加一个launch 块?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多