Zum Hauptinhalt springen

🚀 Warum Coroutines?

📖 Was sind Coroutines?

Coroutines sind eine Kernfunktion von Kotlin, die asynchrone Programmierung einfach und intuitiv macht. Sie können der komplexen Callback-Hölle entkommen und Code schreiben, als wäre er synchron!

💡 Probleme der traditionellen Asynchronität

Callback-Hölle

// ❌ Callback-Ansatz (schwer zu lesen und zu warten)
fun fetchUser(userId: String, callback: (User?) -> Unit) {
fetchFromNetwork(userId) { result ->
if (result != null) {
fetchProfile(result.id) { profile ->
if (profile != null) {
fetchPosts(profile.id) { posts ->
callback(User(result, profile, posts))
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}

Die Kosten von Threads

// ❌ Threads sind teuer
fun downloadFiles() {
repeat(1000) {
Thread {
// Jeder Thread verbraucht viel Speicher
downloadFile("file_$it.txt")
}.start()
}
// Möglicher Speichermangel!
}

✨ Vorteile von Coroutines

1. Prägnanter Code

// ✅ Coroutines (leicht zu lesen und intuitiv)
suspend fun fetchUser(userId: String): User? {
val result = fetchFromNetwork(userId) ?: return null
val profile = fetchProfile(result.id) ?: return null
val posts = fetchPosts(profile.id)
return User(result, profile, posts)
}

2. Leichtgewichtige Nebenläufigkeit

// ✅ Auch Tausende von Coroutines sind kein Problem
suspend fun downloadFiles() {
coroutineScope {
repeat(10000) {
launch {
// Leichtgewichtige Coroutine!
downloadFile("file_$it.txt")
}
}
}
}

3. Strukturierte Nebenläufigkeit

// ✅ Automatische Lebenszyklusverwaltung
suspend fun processData() {
coroutineScope {
val job1 = launch { task1() }
val job2 = launch { task2() }

// Wird automatisch abgeschlossen, wenn alle Aufgaben beendet sind
}
}

🎯 Praktische Anwendungsbeispiele

Netzwerkanfragen

// Sauber ohne Callbacks!
suspend fun loadUserData(userId: String): UserData {
val user = apiService.getUser(userId)
val posts = apiService.getPosts(userId)
val friends = apiService.getFriends(userId)

return UserData(user, posts, friends)
}

UI-Aktualisierung

// Ohne den Haupt-Thread zu blockieren
suspend fun updateUI() {
val data = withContext(Dispatchers.IO) {
// Daten im Hintergrund laden
loadDataFromDatabase()
}

// UI im Haupt-Thread aktualisieren
updateViews(data)
}

Parallele Verarbeitung

// Mehrere Aufgaben gleichzeitig!
suspend fun fetchAllData(): CombinedData = coroutineScope {
val users = async { fetchUsers() }
val products = async { fetchProducts() }
val orders = async { fetchOrders() }

CombinedData(
users.await(),
products.await(),
orders.await()
)
}

🔥 Coroutines vs Threads

MerkmalCoroutinesThreads
GewichtSehr leichtSchwer
ErstellungskostenFast keineTeuer
AnzahlZehntausende möglichTausende Limit
WechselkostenNiedrigHoch
AbbruchEinfachSchwierig

Leistungsvergleich

// Coroutines: 100.000 ausführen
suspend fun testCoroutines() {
val time = measureTimeMillis {
coroutineScope {
repeat(100_000) {
launch {
delay(1000)
}
}
}
}
println("Coroutines: ${time}ms") // ~1000ms
}

// Threads: Schon bei 10.000...
fun testThreads() {
val time = measureTimeMillis {
repeat(10_000) {
Thread {
Thread.sleep(1000)
}.start()
}
}
println("Threads: ${time}ms") // Möglicher Speichermangel!
}

🎨 Kernkonzepte von Coroutines

Suspend (Unterbrechung)

// suspend-Funktionen können nur innerhalb von Coroutines aufgerufen werden
suspend fun doWork() {
delay(1000) // Wartet, ohne den Thread zu blockieren
println("Arbeit abgeschlossen")
}

Strukturierte Nebenläufigkeit

// Lebenszyklusverwaltung durch Eltern-Kind-Beziehung
fun main() = runBlocking {
launch { // Eltern
launch { // Kind 1
delay(1000)
println("Kind 1 abgeschlossen")
}
launch { // Kind 2
delay(2000)
println("Kind 2 abgeschlossen")
}
println("Eltern wartet, bis die Kinder fertig sind")
}
}

🤔 Häufig gestellte Fragen

F1. Sind Coroutines neue Threads?

A: Nein! Coroutines sind leichtgewichtige Arbeitseinheiten, die auf Threads laufen.

// Mehrere Coroutines können auf einem Thread ausgeführt werden
fun main() = runBlocking {
repeat(3) { i ->
launch {
println("Coroutine $i: ${Thread.currentThread().name}")
}
}
}
// Alle können auf demselben Thread ausgeführt werden!

F2. Müssen alle Funktionen als suspend deklariert werden?

A: Nein! Nur Funktionen, die asynchrone Operationen benötigen, sollten suspend sein.

// ❌ Nicht notwendig
suspend fun add(a: Int, b: Int) = a + b

// ✅ Notwendig
suspend fun fetchData() = withContext(Dispatchers.IO) {
loadFromNetwork()
}

F3. Wann sollten Coroutines verwendet werden?

A: Verwenden Sie sie in diesen Fällen!

// ✅ Netzwerkanfragen
suspend fun loadData() = api.fetchData()

// ✅ Datenbankoperationen
suspend fun saveUser(user: User) = db.insert(user)

// ✅ Datei-I/O
suspend fun readFile(path: String) = withContext(Dispatchers.IO) {
File(path).readText()
}

// ✅ Langwierige Berechnungen
suspend fun heavyComputation() = withContext(Dispatchers.Default) {
// Komplexe Berechnungen
}

🎬 Abschließend

Machen Sie asynchrone Programmierung mit Coroutines einfach!

Zusammenfassung:
✅ Flucht aus der Callback-Hölle
✅ Leichtgewichtige Nebenläufigkeit
✅ Intuitiver Code
✅ Strukturierter Lebenszyklus
✅ Viel effizienter als Threads

Nächster Schritt: Probieren Sie Coroutines tatsächlich in Grundlagen der Coroutines aus!