Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

What I want to do is to use kotlin coroutines for database operations and show users a loading screen in the meantime. My basic implementation is as follows:

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

This works perfectly for me when the loading takes quite some time for large datasets. But in scenarios where the fetchDataFromDatabase() finishes quickly, showing and hiding the dialog box in quick succession creates an annoying glitching effect.

So what I want is to show the dialog box only if the fetchDataFromDatabase() function take more than, lets say, 100 ms to complete.

So my question is, what is the performance efficient way to achieve this using kotlin coroutines?

like image 250
Ashu Avatar asked Nov 21 '19 10:11

Ashu


2 Answers

Here's an idea:

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

When you cancel the dialogJob, it should either hit the delay statement and prevent showing the dialog, or the join statement, which will cause the finally block to execute and hide it.

like image 154
Marko Topolnik Avatar answered Oct 16 '22 07:10

Marko Topolnik


Here is how I have achieved this, without using !! not null operator:

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)
    }
}
like image 41
Ashu Avatar answered Oct 16 '22 06:10

Ashu