1/112
Looks like no tags are added yet.
Name | Mastery | Learn | Test | Matching | Spaced | Call with Kai |
|---|
No analytics yet
Send a link to your students to track their progress
Was ist Kotlin?
Moderne Programmiersprache, seit 2019 bevorzugte Sprache für Android-Apps, kompiliert zu Java-Bytecode (Java-kompatibel). Vorteile: expressive & concise, safer code (Null-Safety), interoperabel mit Java, Structured Concurrency
val vs var
val = read-only/immutable (z.B. val popcorn: Int = 5), var = mutable/veränderbar. Kotlin ist statisch typisiert; Immutabilität wird nicht erzwungen, aber empfohlen
Type Inference
Der Typ wird vom Compiler oft automatisch erkannt, z.B. var popcorn = 5 wird automatisch Int
Null Safety in Kotlin
Variablen können standardmäßig NICHT null sein. Drei Operatoren: Safe Call ?, Non-Null Assertion !!, Elvis ?:
Safe Call Operator ?
Erlaubt null und führt den Aufruf nur aus, wenn der Wert nicht null ist; sonst ergibt der Ausdruck null (keine NullPointerException). Bsp: var x: Int? = null
Non-Null Assertion !!
Erzwingt non-null und wirft eine NullPointerException, falls der Wert null ist
Elvis Operator ?:
Liefert einen Default-Wert, falls der linke Ausdruck null ist. Bsp: x?.dec() ?: 0
when (Kotlin)
Wie ein switch, aber mächtiger; kann auch als Expression einen Wert zurückgeben
Ranges in for-Loops
1..5 -> 12345;
5 downTo 1 -> 54321;
3..6 step 2 -> 35;
'd'..'g' -> defgCollections: List, Set, Map
List = geordnet, Duplikate erlaubt; Set = ungeordnet, nur eindeutige Elemente; Map = Key-Value-Paare mit eindeutigen Keys
Read-only vs Mutable Collections
Read-only: listOf(), setOf(), mapOf(); Mutable: mutableListOf(), mutableSetOf(), mutableMapOf()
Lambda
Eine Funktion ohne Namen, z.B. { level: Int -> level / 2 }
Higher-Order Function
Eine Funktion, die eine Funktion als Parameter nimmt ODER eine Funktion zurückgibt. Function Type z.B. (Int) -> Int; Referenz auf benannte Funktion mit ::funcName
Last Parameter Call Syntax
Wenn der letzte Parameter einer Funktion eine Funktion ist, kann die Lambda außerhalb der Klammern (als Trailing Lambda) geschrieben werden, z.B. repeat(3) { println("Hello") }
Single-Expression Funktion
Kompakte Funktion mit = statt Body, z.B. fun double(x: Int): Int = x * 2
Unit-Funktion
Eine Funktion, die keinen sinnvollen Wert zurückgibt; der Return-Typ ist Unit (entspricht void)
Konstruktoren in Kotlin
Primary Constructor steht im Klassen-Header (z.B. class Circle(val radius: Double)); init { } läuft beim Erstellen (mehrfach möglich); sekundäre Konstruktoren mit constructor(…) müssen den primären via this(…) aufrufen; Default-Parameter ersetzen oft mehrere Konstruktoren
Extension Function
Fügt einer existierenden Klasse eine Funktion hinzu, ohne sie zu ändern. Bsp: fun Int.isOdd(): Boolean { return this % 2 == 1 }
Data Class
Klasse zum Speichern von Daten (Schlüsselwort data). Generiert automatisch: Getter/Setter, toString(), equals(), hashCode(), copy() und Destrukturierungs-Operatoren. Bsp: data class Player(val name: String, val score: Int)
Enum Class
User-definierter Typ mit festen Werten, z.B. enum class Color { RED, GREEN, BLUE }; Werte können auch Parameter haben
Object (Singleton)
Es gibt nur EINE Instanz. Statt class schreibt man object, z.B. object Calculator { fun add(a: Int, b: Int) = a + b }; Aufruf via Calculator.add(2, 4)
Companion Object
Alle Instanzen einer Klasse teilen sich diese Variablen/Funktionen (wie static in Java). Schlüsselwort companion; Zugriff via ClassName.property
Sealed Class
Schränkt Vererbung auf eine vordefinierte Menge von Subklassen ein, deren Typen zur Compile-Zeit bekannt sind. Mächtiger als Enums (jede Subklasse kann eigene Daten/Implementierung haben) und perfekt mit when (kein else nötig)
Vererbung in Kotlin
Single-Parent-Inheritance; Klassen sind final by default (mit open öffnen, um Vererbung zu erlauben); Überschreiben mit override; Interfaces erlauben Mehrfach-Implementierung
minSdk vs targetSdk
minSdk = minimale API-Version (möglichst viele Nutzer erreichen); targetSdk = neue Apps müssen die aktuellste Major-Version targeten (Google-Play-Anforderung)
Android Architektur (Layers von unten)
Linux Kernel -> HAL (Hardware Abstraction Layer) -> Native C/C++ Libraries + Android Runtime (ART) -> Java API Framework (Manager-Klassen) -> System Apps
Android Security Sandbox
Jede App ist ein eigener User und kann nur eigene Dateien lesen; jeder Prozess läuft in einer eigenen VM (Isolation)
Android SDK
Enthält alle Libraries und Tools für eine bestimmte Android-Version
App-Komponenten
Activity (Einstiegspunkt für User-Interaktion, ein Screen mit UI); Service (läuft im Hintergrund ohne UI, z.B. Musik); Broadcast Receiver (reagiert auf System-Events wie Akku schwach); Content Provider (verwaltet geteilte App-Daten, z.B. Kontakte)
Jetpack
Suite von Libraries für Best Practices, weniger Boilerplate, versionsübergreifend; "unbundled" (einzeln einbindbar); liegen im Namespace androidx.* (z.B. Compose, WorkManager, Room, Navigation, CameraX)
Jetpack Compose
Modernes deklaratives UI-Toolkit für Android. Vorteile: weniger Code, intuitiv, schnellere Entwicklung durch Wiederverwendung, Built-in Support für Material Design/Animationen/Themes
Imperativ vs Deklarativ
Imperativ (altes XML-System): XML-Layout + findViewById/setText, man sagt wie. Deklarativ (Compose): man sagt was man sehen will; Composables sind stateless, UI wird durch erneutes Aufrufen mit neuen Argumenten aktualisiert
Composable Function
Mit @Composable annotiert, nimmt Daten als Parameter, Rückgabetyp ist immer Unit. Bsp: @Composable fun Greeting(name: String) { Text("Hello $name") }
Layout-Komponenten in Compose
Column (vertikal anordnen), Row (horizontal anordnen), Box (übereinander stapeln)
Modifier
Kotlin-Objekte, die Composables dekorieren (Verhalten, Aussehen, User-Input verarbeiten) und Interaktionen definieren (clickable, scrollable, draggable, padding, fillMaxWidth)
3 Charakteristiken von Modifiern
Können geändert (changed), wiederverwendet (reused) und verkettet (chained) werden
LazyColumn vs Column
Column { items.forEach } für wenige Elemente ohne Scrollen; LazyColumn/LazyRow für viele/unbekannt viele Elemente, da nur sichtbare Items zusammengebaut werden (bessere Performance)
State (Compose)
Jeder Wert, der sich über die Zeit ändern kann (Counter, Scroll-Position, Progress). State bestimmt, was im UI angezeigt wird, und wird durch Events (Klick, Sensor, Netzwerk) geändert
Composition
Definiert, wie das UI aufgebaut wird, wenn Compose die Composable-Funktionen das erste Mal ausführt
Recomposition
Wenn sich beobachteter State ändert, ruft Compose die betroffenen Composables mit den neuen Werten erneut auf. Sie ist optimistisch (kann bei Parameteränderung abgebrochen/neu gestartet werden)
Skipping
Compose überspringt alle Composables, deren Eingabe-Parameter sich nicht geändert haben, und recomposed nur die wirklich betroffenen
mutableStateOf
Erzeugt ein observable State-Holder-Objekt, das Compose trackt; eine normale Variable löst KEINE Recomposition aus
remember
Erinnert sich an einen Wert über Recompositions hinweg (ohne remember würde der Wert bei jeder Recomposition zurückgesetzt)
rememberSaveable
Wie remember, überlebt aber zusätzlich Configuration Changes (z.B. Rotation)
by (Delegate in Compose)
Delegate, der direkten Zugriff auf den State-Wert ohne .value erlaubt, z.B. var name by remember { mutableStateOf("") }
Eigenschaften von Composables
Können in beliebiger Reihenfolge und parallel laufen, dürfen keine Side-Effects haben, können sehr häufig aufgerufen werden (z.B. jeder Animationsframe); Recomposition ist optimistisch und überspringt so viel wie möglich
State Hoisting
Pattern, bei dem State von einer Composable nach oben in den Caller verschoben wird -> Composable wird stateless. Statt interner Variable nutzt man value: T und onValueChange: (T) -> Unit
Vorteile von State Hoisting
Single Source of Truth (keine Duplikation), Shareable (von mehreren Composables nutzbar), Interceptable (Caller kann Events abfangen/ändern), Decoupled (State kann z.B. im ViewModel liegen)
3 Regeln des State Hoisting
1) State auf den lowest common parent aller Composables heben, die ihn lesen; 2) auf das höchste Level heben, auf dem er geändert wird; 3) zwei States, die auf dasselbe Event reagieren, auf dasselbe Level heben
Unidirektionaler Datenfluss (UDF)
State fließt nach unten, Events fließen nach oben
Navigation Back Stack
Stack von Destinations: Start-Screen ist die Basis (unten), aktueller Screen ist oben. Navigieren = Destination wird auf den Stack gepusht; Zurück = oberste Destination wird gepoppt. Der NavController verwaltet den Back Stack automatisch
Jetpack Navigation – 3 Hauptteile
NavController (navigiert zwischen Destinations via navController.navigate(route)); NavGraph (mappt Composables auf Routes); NavHost (Container-Composable, zeigt die aktuelle Destination)
Route vs Destination
Route = String, der eine Destination eindeutig identifiziert (wie eine URL); Destination = einzelnes Composable (oder Gruppe), das einen Screen darstellt
Warum Sealed Class statt Strings für Routes?
Strings sind tippfehleranfällig (Fehler erst zur Laufzeit); Enum/Sealed Class garantiert Compile-Time-Safety und Routes sind zentral und einfach refactorbar
Argumente in Navigation weitergeben
1) Placeholder in Route ("profile/{userId}"); 2) optional Typ via navArgument definieren; 3) Argument aus backStackEntry.arguments lesen; 4) beim Navigieren Wert übergeben (navController.navigate("profile/$userId"))
popBackStack inclusive
navController.popBackStack(route, inclusive): inclusive=true entfernt auch die angegebene Route, inclusive=false lässt sie stehen
AI Agents – Acceptance Bar
Vorschläge kritisch prüfen: korrekt? Failures behandelt? passt zum Architektur-Style? effizient?
AI Agents – Red Flags
Neue Dependencies, unsichere Patterns ("disable SSL"), catch(Exception) ohne Reaktion, viel Code für kleine Änderung, keine Tests, Blocking-Calls in async Code
AGENTS.md
Datei mit projektspezifischen Anweisungen, die der AI-Agent bei jeder Interaktion berücksichtigt (z.B. "Use ViewModels for business logic", "Use Hilt for DI")
Activity Lifecycle Callbacks
onCreate (erstellt), onStart (sichtbar), onResume (hat Fokus, interagierbar), onPause (verliert Fokus), onStop (nicht mehr sichtbar), onDestroy (zerstört)
Warum Lifecycle-aware?
Vermeidet Crashes (z.B. Anruf während Nutzung), spart System-Ressourcen (Kamera/Sensor stoppen im Hintergrund), verhindert Verlust des Fortschritts. Bsp: Standort-Updates stoppen, Buffering pausieren
Types of UI State & Logic
Screen UI State (anzuzeigende Daten, z.B. Artikel-Liste); UI Element State (Properties einzelner Elemente, z.B. expanded-Flag); Business Logic (Anforderungen an App-Daten, z.B. bookmarken+DB); UI Logic (wie State angezeigt wird, z.B. Card expandieren, Navigation)
ViewModel
Lifecycle-aware Komponente, die UI vom State trennt; Source of Truth für UI State; überlebt Configuration Changes; wird im Navigation-Backstack gecached und beim Verlassen aufgeräumt; gute Jetpack-Integration (Hilt, Navigation)
2 Aufgaben eines ViewModels
1) Zugriff auf Business Logic (oft via Repository); 2) App-Daten für die UI vorbereiten (UI State)
ViewModel – wichtige Regeln
Sollte keinen State aus Composables nehmen (lebt länger als die Composition); State muss trackbar sein (Compose State APIs wie mutableStateOf); im Composable via viewModel() oder hiltViewModel(); beim Konsumieren kein remember nötig
MVVM in Android
UI Layer = UI Elements + State Holders; UI State Management wird oft an ViewModels delegiert
ViewModel vs plain State Holder
Plain State Holder: UI Logic & UI Element State, überlebt KEINE Config Changes, im Composable erstellt+remembered. ViewModel: Screen UI State + Business-Logic-Zugriff, überlebt Config Changes, scoped an Activity/NavGraph
Lifecycle im ViewModel tracken
ViewModel implementiert DefaultLifecycleObserver (override onPause/onStart); im Composable via LocalLifecycleOwner.current + DisposableEffect den Observer hinzufügen/entfernen (onDispose)
3 Architektur-Prinzipien
Separation of Concerns (jeder Teil eine Aufgabe); Single Source of Truth (nur der Eigentümer verändert Daten, gibt sie immutable nach außen); Unidirectional Data Flow (State runter, Events rauf)
Architektur-Layer
UI Layer (Daten anzeigen, auf Interaktion reagieren); Domain Layer (optional, komplexe geteilte Business Logic / Use Cases); Data Layer (Business Logic, Daten erstellen/speichern/ändern)
Repository Pattern
Isoliert den Data Layer und ist dessen einziger Entry Point. Aufgaben: Daten bereitstellen, Änderungen zentralisieren, Konflikte zwischen Sources auflösen, Sources abstrahieren, Business Logic enthalten. Andere Layer dürfen NIEMALS direkt auf Data Sources zugreifen; Daten werden immutable nach außen gegeben
Data Source
Arbeitet je mit genau EINER Quelle (File, API oder lokale DB); ein Repository kann mehrere Data Sources kombinieren
Threading / main-safe
Aufrufe an Repos/Data Sources sollten main-safe sein; lange Operationen auf Background-Threads auslagern (Coroutines, suspend); Datenänderungen via Kotlin Flow beobachten
Storage Types in Android
App-specific Storage (nur diese App, intern verschlüsselt oder extern); Shared Storage (mit anderen Apps geteilt, via MediaStore-API); Preferences (SharedPreferences, Key-Value); Databases (strukturierte Daten, SQLite + Room)
SharedPreferences
Privater Key-Value-Storage für kleine, primitive User-Settings (z.B. "theme" -> "dark"). Zugriff via getPreferences + edit() + putInt(…) + apply()
Room
Jetpack-Library und Wrapper über SQLite. Vorteile: vereinfachtes Setup, Arbeit mit Data Classes, Compile-Time-Verifikation von SQL-Queries, annotation-basiert (weniger Boilerplate)
3 Hauptkomponenten von Room
Entity (repräsentiert eine Tabelle), DAO/Data Access Object (Methoden für CRUD-Operationen), Database (stellt DAO-Instanzen bereit)
Entity (Room)
Mit @Entity(tableName="…") annotierte Data Class; @PrimaryKey markiert den Schlüssel; @ColumnInfo(name="…") benennt eine Spalte um
DAO (Data Access Object)
Interface mit @Dao, das die CRUD-Methoden für den DB-Zugriff definiert. Room generiert die Implementierung zur Compile-Zeit und prüft alle SQL-Queries. Kann Flow zurückgeben für Live-Updates
Room-Annotationen
@Insert/@Update/@Delete sind Convenience-Annotationen (kein eigenes SQL nötig); @Query("…") für eigenes SQL; lange Operationen als suspend; Stream-Beobachtung gibt Flow
TypeConverters
Wenn Room einen Typ (z.B. Liste, Date) nicht unterstützt, schreibt man Converter-Methoden mit @TypeConverter und bindet sie via @TypeConverters(Converters::class) in die DB-Klasse ein
Data Layer einbauen – Schritte
1) Modell mit @Entity annotieren; 2) DAO-Interface mit CRUD + @Dao erstellen; 3) abstrakte DB-Klasse mit @Database (Entity + DAO verbinden); 4) Repository erstellen, das das DAO injectet
Dependency Injection (DI)
Klassen brauchen Referenzen auf andere Klassen (Dependencies). Statt sie selbst zu erzeugen, werden sie injiziert (als Parameter übergeben). Manuelle DI = viel Boilerplate; Libraries wie Hilt automatisieren das
Hilt
Offizielle DI-Lösung für Android (static, compile time). Reduziert DI-Boilerplate massiv und funktioniert nahtlos mit ViewModels
Hilt – Setup-Schritte
1) Application-Klasse mit @HiltAndroidApp; 2) Activities mit @AndroidEntryPoint; 3) ViewModels mit @HiltViewModel + @Inject constructor; 4) Hilt-Modul (@Module + @InstallIn) für nicht direkt injectbare Klassen (Room-DB, Retrofit); 5) im Composable hiltViewModel()
Hilt-Annotationen
@HiltAndroidApp (Application), @AndroidEntryPoint (Activity), @HiltViewModel (+@Inject constructor), @Module + @InstallIn (Modul), @Provides/@Singleton (bereitstellen)
Hilt – Vorteile
Weniger Boilerplate (kein ViewModelProvider.Factory mehr), automatische Injection, bessere Testbarkeit (Mocks), Lifecycle-Management, offizieller Android-Support
Retrofit
Library für RESTful APIs: mappt URI & HTTP-Methode auf Kotlin-Funktionen und JSON-Antworten auf Kotlin-Objekte. API-Methoden werden in einem Interface mit @GET/@Query etc. definiert
DTO & Mapping
Wenn API-Response und DB-Entity sich unterscheiden, schreibt man ein DTO + Mapping-Funktion (z.B. fun QuestionResponse.toQuestion(): Question)
Coroutine
Concurrency-Pattern zur Vereinfachung von async Code; verhindert, dass lange Tasks den Main-Thread blockieren. Im ViewModel via viewModelScope.launch { }
Suspend Function
Funktion, die blockieren kann und nur innerhalb einer Coroutine aufrufbar ist. Bsp: suspend fun expensiveComputation() { delay(1000L) }
Flow
Stream von Daten, der asynchron mehrere Werte über die Zeit emittiert. Wird in Coroutines konsumiert; ideal für Live-Updates aus DB oder Netzwerk (passt gut zu Room)
3 Teile eines Flow
Producer (erzeugt Werte, emit(…)); Intermediate Operators (verändern/filtern lazy: map, filter, onEach); Terminal Operator/Consumer (startet & konsumiert den Flow, z.B. collect { })
Flow in Jetpack
Room-DAO kann Flow
Netzwerk-Permissions (AndroidManifest)
Pair Programming – Driver
Hat die Tastatur, fokussiert sich auf das aktuelle kleine Ziel und spricht laut über das, was er gerade macht
Pair Programming – Navigator
Beobachtet, reviewed Code on-the-go, denkt mittel- bis langfristig und notiert nächste Schritte / mögliche Probleme. Rollen regelmäßig wechseln!
Pair Programming – Vorteile
Wissens-Sharing, Reflektion, Code Review on-the-go, höhere Fokussiertheit, 2 Denkmodi parallel, Collective Code Ownership, weniger WIP -> höhere Qualität & besserer Team-Flow
ExoPlayer & Lifecycle (Altklausur)
Player in onStart()/onResume() initialisieren bzw. wieder anhängen, in onStop()/onPause() pausieren und mit player.release() freigeben. Playback-Position im ViewModel halten, damit sie Config Changes überlebt und kein Memory Leak / Crash bei Rotation entsteht
Warum nicht auf dem Main-Thread? (Data Layer)
Der Main-/UI-Thread zeichnet UI und verarbeitet Eingaben und muss frei bleiben (~60 fps). DB-Queries und Netzwerk-Requests sind blockierende Langläufer -> sie würden den Thread blockieren (UI friert ein, ANR-Dialog, Crash). Deshalb main-safe via Coroutines/Dispatchers.IO