jambit ToiletPaper 112

Kotlin Coroutinen

Problem

Bestimmte Aktionen sollen im Hintergrund ausgeführt werden, um den Main-Thread der App nicht zu blockieren. Typischerweise werden dafür Threads oder AsyncTasks verwendet: Die Aktion wird im Background ausgeführt und schließlich wird das Ergebnis auf dem Main-Thread dargestellt.

Lösung

Kotlin bietet einen etwas eleganteren Weg mit Coroutinen an. Coroutinen sind leichtgewichtige Threads, die auf einen von Kotlin verwalteten Thread-Pool dispatched werden, sofern man vorhandene Dispatcher verwendet. Vergleichbar ist das Ganze mit GCD von iOS.

Beispiel

Ein Image soll heruntergeladen und dargestellt werden:

private fun downloadImageWithCoroutines() {
    GlobalScope.launch(Dispatchers.Main) {
        binding.status.text = "Downloading image ..."
        // Download the image on an IO dispatcher without blocking the main thread
        val image = withContext(Dispatchers.IO) { downloadImage() }
        // Continue on the main thread
        binding.image.setImageDrawable(image)
    }
}

Zunächst wird im oberen Beispiel auf dem Main-Thread der Status gesetzt, anschließend erfolgt ein asynchroner Download des Images. Die Besonderheit dabei ist, dass der Main-Thread nicht blockiert wird, sondern erst, wenn das Ergebnis verfügbar ist, läuft der restliche Code auf dem Main-Thread weiter.

Technisch wird das von Kotlin so gelöst, dass der Code nach dem withContext als Continuation ausgeführt wird. Man könnte die downloadImage Methode also so umschreiben, dass diese das Ergebnis einer Continuation übergibt und nicht direkt zurückliefert. Natürlich muss sich hier der Aufrufer der Methode um das entsprechende Threading kümmern:

private fun downloadImage(continuation: (Drawable?) -> Unit) {
    val drawable = url.openStream()?.use {
        Drawable.createFromStream(it, "image")
    }
    continuation(drawable)
}

Eine besondere Stärke ist die Kombination mehrerer async Calls. Die async-Calls können hier gleichzeitig auf unterschiedlichen IO-Threads ausgeführt werden:

// Start multiple requests
val image1 = async(Dispatchers.IO) { downloadImage1() }
val image2 = async(Dispatchers.IO) { downloadImage2() }
// Wait for the results and show the images
binding.image1.setImageDrawable(image1.await())
binding.image2.setImageDrawable(image2.await())

Weiterführende Aspekte

---

Autor: Marco Pfattner / Senior Software Architect/ Business Division Automotive Bavaria

Zum Toilet Paper #112: Kotlin Coroutinen (pdf)

Kotlin Coroutines jambit ToiletPaper

Wir verwenden Cookies, um unsere Webseite für Sie zu optimieren. Mit dem Besuch unserer Webseite erklären Sie sich damit einverstanden. // Our website is using cookies to improve your experience. By continuing to browse the site, you are agreeing to our use of cookies.

Weitere Informationen finden Sie in unserer Datenschutzerklärung. // For more information, please refer to our privacy policy.