如果 kotlin 协程作业需要相当长的时间才能完成,如何有效地显示加载对话框?

How to efficiently show loading dialog if kotlin coroutine job takes quite some time to complete?

我想做的是使用kotlin协程进行数据库操作,同时给用户展示一个加载界面。我的基本实现如下:

fun loadSomeData(){
    mainCoroutineScope.launch { 
        showLoadingDialog()
        // suspening function over Dispatchers.IO, returns list
        val dataList = fetchDataFromDatabase()
        inflateDataIntoViews(dataList)
        hideLoadingDialog()
    }
}

当加载大型数据集需要相当长的时间时,这对我来说非常有用。但是在 fetchDataFromDatabase() 快速完成的情况下,快速连续显示和隐藏对话框会产生令人讨厌的故障效果。

所以我想要的是仅当 fetchDataFromDatabase() 函数花费超过 让我们说 ,100 毫秒才能完成时才显示对话框。

所以我的问题是,使用 kotlin 协程实现此目的的性能高效方法是什么?

这是一个想法:

fun loadSomeData(){
    mainCoroutineScope.launch {
        val dialogJob = launch {
            delay(1000)
            try {
                showLoadingDialog()
                coroutineContext.job.join()
            } finally {
                hideLoadingDialog()
            }
        }
        val dataList = fetchDataFromDatabase()
        inflateDataIntoViews(dataList)
        dialogJob.cancel()
    }
}

当您取消 dialogJob 时,它应该点击 delay 语句并阻止显示对话框,或者 join 语句,这将导致 finally阻止执行并隐藏它。

加载对话框的主要目的是防止用户在加载时触摸UI,但不能保证这一点。在弹出对话框之前,用户总是有机会触摸某个按钮。

更好的方法是禁用或隐藏 UI 组件,或者通常显示 UI.

的 "loading version"

最好让用户取消加载而不是设置一个短的超时时间,所以你可能仍然需要一个显示取消按钮的对话框或小吃栏,或者你可以在你的应用程序中制作一个任务管理器页面,那将是虽然真的很复杂。

我是这样实现的,没有使用 !! not null 运算符:

val deferred = lifecycleScope.async(Dispatchers.IO) {
    // perform possibly long running async task here
}

lifecycleScope.launch (Dispatchers.Main){
    // delay showing the progress dialog for whatever time you want
    delay(100)

    // check if the task is still active
    if (deferred.isActive) {

        // show loading dialog to user if the task is taking time
        val progressDialogBuilder = createProgressDialog()

        try {
            progressDialogBuilder.show()

            // suspend the coroutine till deferred finishes its task
            // on completion, deferred result will be posted to the
            // function and try block will be exited.
            val result = deferred.await()
            onDeferredResult(result)

        } finally {
            // when deferred finishes and exits try block finally
            // will be invoked and we can cancel the progress dialog
            progressDialogBuilder.cancel()
        }
    } else {
        // if deferred completed already withing the wait time, skip
        // showing the progress dialog and post the deferred result
        val result = deferred.await()
        onDeferredResult(result)
    }
}