Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait until Kotlin coroutine finishes in onCreateView()

I have an initialization block in onCreateView, where some variables are assigned from SharedPreferences, DB or Network (currently from SharedPreferences).

I want to update views with these values in onViewCreated. But they update with empty values before a coroutine in onCreateView finishes. How to wait until the coroutine finishes in main thread?

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    ...
    GlobalScope.launch(Dispatchers.Main) {
        val job = GlobalScope.launch(Dispatchers.IO) {
            val task = async(Dispatchers.IO) {
                settingsInteractor.getStationSearchCountry().let {
                    countryName = it.name
                }
                settingsInteractor.getStationSearchRegion().let {
                    regionName = it.name
                }
            }
            task.await()
        }
        job.join()
    }
}

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

    country.updateCaption(countryName)
    region.updateCaption(regionName)
}

UPDATE (20-05-2019)

Currently I don't use onViewCreated. I write code in onCreate and onCreateView. In onCreateView I access views this way: view.country.text = "abc" and so on.

like image 905
CoolMind Avatar asked Dec 07 '18 08:12

CoolMind


People also ask

How do you wait for coroutine to finish Kotlin?

When we launch a coroutine in Kotlin using launch we are returned the resulting Job . We can wait for the coroutine to finish by calling join() on the Job.

How do you abort coroutines?

cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.") like the following example shows: val startTime = System. currentTimeMillis() val job = launch(Dispatchers.

Why is coroutine faster than thread?

They tend to slow down when there are a lot of tasks being run, which is why using coroutines help to speed things up. Threads run tasks in serial, while coroutines runs many tasks at the same time.

Do coroutines run concurrently?

Coroutines can be executed concurrently using a multi-threaded dispatcher like the Dispatchers.


2 Answers

It's better to use async instead of launch https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html.

Create lateinit val dataLoad: Deferred<Pair<String, String>. init it as dataLoad = GlobalScope.async(Dispatchers.Defailt) {} in onCreateView or earlier. launch new coroutine in UI scope in onViewCreated. Wait for result in that coroutine val data = dataLoad.await() and then apply data to ui

like image 90
logcat Avatar answered Oct 04 '22 21:10

logcat


In your case you don't need to use GlobalScope as a coroutine context (you can but it is not recommended as per docs). You need a local scope:

private var job: Job = Job()
override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + job

@Override
public void onDestroy() {
    super.onDestroy();
    job.cancel()
}

Also your fragment should implement CoroutineScope and to use Dispatchers.Main in Android add dependency to app's build.gradle:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

The code to wait until the coroutine finishes:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    launch {
        val operation = async(Dispatchers.IO) {
            settingsInteractor.getStationSearchCountry().let {
                countryName = it.name
            }
            settingsInteractor.getStationSearchRegion().let {
                regionName = it.name
            }
        }
        operation.await() // wait for result of I/O operation without blocking the main thread

        // update views
        country.updateCaption(countryName)
        region.updateCaption(regionName)
    }
}

EDIT:

In Activity or Fragment you can use lifecycleScope instead of implementing CoroutineScope:

lifecycleScope.launch { ... }

To use lifecycleScope add next line to dependencies of the app's build.gradle file:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION"

At the time of writing final LIFECYCLE_VERSION = "2.3.0-alpha05"

like image 28
Sergey Avatar answered Oct 04 '22 20:10

Sergey