🚀 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
| Merkmal | Coroutines | Threads |
|---|---|---|
| Gewicht | Sehr leicht | Schwer |
| Erstellungskosten | Fast keine | Teuer |
| Anzahl | Zehntausende möglich | Tausende Limit |
| Wechselkosten | Niedrig | Hoch |
| Abbruch | Einfach | Schwierig |
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!