Logo hi.health

hi.health

Startup

Declarative UI on Android

Description

Adrian Tappe von hi.health spricht in seinem devjobs.at TechTalk über deklarative UI on Android und zeigt einige Beispiele für Compose.

Beim Videoaufruf stimmst Du der Datenübermittlung an YouTube und der Datenschutzerklärung zu.

Video Zusammenfassung

In "Declarative UI on Android" zeigt Adrian Tappe die Schwächen des XML-basierten Android-UIs (duplizierter Zustand, nicht typsichere ID-Zugriffe, Lifecycle-Fallen) und stellt dem den deklarativen, Kotlin-zentrierten Ansatz von Compose gegenüber. An der „High Clickable“-Demo erläutert er Composable-Funktionen, die UI emittieren, reaktive Recomposition mit remember für UI-nahen Zustand, normalen Kotlin-Control-Flow und die Interoperabilität zwischen Compose und bestehenden XML-Views. Er warnt vor dem Beta-Status und enger Versionskopplung mit daraus resultierender Build-Instabilität und empfiehlt eine schrittweise Migration über die Interop, sobald Stabilität gegeben ist.

Declarative UI auf Android: Compose statt XML – Erkenntnisse aus „Declarative UI on Android“ von Adrian Tappe (hi.health)

Kontext und Kernbotschaft des Talks

In „Declarative UI on Android“ präsentierte Adrian Tappe von hi.health eine klare, praxisnahe Argumentation: Der klassische XML-Ansatz zur UI-Entwicklung auf Android ist uns vertraut, aber es ist an der Zeit, das deklarative Paradigma zu umarmen. Der Talk führte Schritt für Schritt durch die Schwächen des traditionellen Systems, die Grundprinzipien von Compose, die Interoperabilität mit bestehendem Code und eine ehrliche Einschätzung des Reifegrads. Wir von DevJobs.at haben besonders genau zugehört, weil dieser Wechsel nicht nur eine neue API mit sich bringt, sondern auch eine neue Denkweise: UI als Funktion von Zustand.

„It’s hard to use that, but it’s time for something new.“ – Der Ton war gesetzt: Der Klassik-Stack funktioniert, doch Compose verschiebt die Grenzen spürbar nach vorn.

Das Problem mit dem klassischen XML-Ansatz

Android-UI wurde lange über XML-Layouts modelliert. Das Prinzip ist vertraut, weit verbreitet und – wie Tappe anmerkt – für Lernen und Adoption durchaus solide. Er vergab dafür sinnbildlich „vier Daumen hoch“, vor allem weil „es das System ist, das wir nutzen“, und wir wissen, „wie wir damit umgehen“.

Trotzdem legt der Talk die konzeptionellen Schwächen offen:

  • Doppelter Zustand: Nach dem Inflaten hat das UI selbst Zustand. Gleichzeitig hat auch das Modell Zustand. Diese Duplizierung zwingt zur Synchronisation – ein fehleranfälliger und wartungsintensiver Teil der UI-Logik.
  • Type-Safety: Elemente werden klassisch via ID gefunden. Das ist nicht typsicher und öffnet Raum für Laufzeitfehler.
  • Lifecycle-Fallen: „Auf einem mobilen Gerät könnte deine UI verschwinden, während du versuchst, sie zuzugreifen.“ Der Lebenszyklus ist volatil, die Gefahr von Nullzugriffen und inkonsistentem UI-Zustand real.
  • Generierte Helfer lösen es nur teilweise: Moderne Werkzeuge können Bindings generieren. Aber, so Tappe, „das ist immer noch ein bisschen flaky und bringt andere Probleme mit“.

In Summe: Der Ansatz ist gelernt und verbreitet – aber mühselig dort, wo UI und Zustand zusammenlaufen. Genau hier setzt Compose an.

Compose in einem Satz: UI als Baum aus Funktionsaufrufen

Tappe führt an einem Beispiel („Hi Clickable“) vor, wie Compose funktioniert: Eine kleine Oberfläche als „Surface“ beziehungsweise „Frame“ mit Logo und Text; ein Klick erhöht die Anzahl der Daumen-hoch-Emojis, bis bei fünf ein Stern-Emoji erscheint. Das Entscheidende ist weniger das konkrete Widget, sondern die Struktur dahinter:

  • „Es ist 100% Kotlin.“ Layouts wie Row und Column, Widgets wie Image und Text – alles sind Funktionsaufrufe.
  • Es entsteht ein „Baum von Funktionsaufrufen“. Genau so fühlt es sich an: verschachtelte, gescopte Funktionsaufrufe statt externer Layoutsprache.
  • Code-zentrisch und ohne Kontextwechsel: „Yay, no context switches.“ Kein Wechsel zwischen Kotlin und XML – ein Editor, ein Sprachkontext.

Warum sich Compose wie eine DSL anfühlt – und trotzdem „nur“ Kotlin ist

Die deklarative Syntax wirkt wie eine DSL. Technisch sind es zwei zentrale Kniffe:

  • Higher-Order Functions und Lambdas: Eine Funktion nimmt eine anonyme Funktion (Lambda) als Parameter entgegen. Diese enthält den UI-Inhalt.
  • Trailing Lambda Syntax: In Kotlin kann das letzte Lambda außerhalb der Klammern geschrieben werden. Das reduziert „closing parenthesis hell“ bei verschachtelten Funktionsaufrufen.

Der Effekt: Natürlich lesbarer UI-Code ohne extrinsisches Layoutformat.

Der zentrale Paradigmenwechsel: Funktionen „emittieren“ UI

Ein Kernpunkt in Tappes Darstellung ist leicht abstrakt, aber entscheidend: Eine Compose-Funktion „liefert“ nicht einfach UI zurück. Sie „emittiert UI während der Ausführung“. Konsequenz:

  • Wird eine Funktion (z. B. für einen „Thumbs up“) zweimal ausgeführt, emittiert sie zweimal das entsprechende UI.
  • Die UI ist eine Funktion von Zustand. „When your state updates … the whole function tree gets re-executed and will emit your new UI.“

Das ist der deklarative Kern: Keine imperative Manipulation eines bereits existierenden, zustandsvollen UI-Objekts. Stattdessen: State ändert sich – Funktion wird ausgeführt – UI wird emittiert.

Zustandsverwaltung dort, wo sie hingehört

Compose erlaubt, zustandsbezogene UI-Aspekte direkt im Funktionsbaum zu „merken“. Tappe nennt als typisches Beispiel den Scroll-Zustand – etwas, das nicht ins Domänenmodell gehört, sondern rein UI-relevant ist. Sinnbildlich: Business-Logik bleibt schlank, UI-nahe Zustände sind lokal handhabbar.

Wiederverwendung: Funktionen sind Komponenten

Ein gewollter Nebeneffekt: In Compose sind Funktionen per se Komponenten. Wer eine anonyme Funktion wiederverwenden möchte, kann in IntelliJ „Extract Method/Function“ nutzen – und hat sofort eine eigenständige Komponente. Das verringert Reibung beim Modularisieren und fördert konsistente UI-Bausteine.

Effizienz ohne manuelles Caching: Compiler-Plugin und Runtime

Eine verständliche Sorge: „Wird bei jeder Zustandsänderung der ganze Baum neu ausgeführt, wird das nicht teuer?“ Tappe adressiert das mit einem Blick in die Compose-Architektur:

  • Compose nutzt ein Compiler-Plugin für Kotlin und eine Runtime, die erkennt, wenn „eine Funktion mit selben Eingaben den gleichen Ausführungspfad und Output hat“. Dann muss sie nicht neu ausgeführt werden.
  • Ergebnis: Bei großem Zustand werden nur jene Teile neu ausgeführt, die sich ändern. Der Rest bleibt unberührt.

Das ist ein wesentlicher Vorteil: Entwicklerinnen und Entwickler schreiben deklarativen, klaren Code – die Laufzeit sorgt für effiziente Aktualisierung.

Volle Kotlin-Kontrollstrukturen im UI-Code

Compose ist „100% Kotlin“. Damit stehen auch Kontrollstrukturen zur Verfügung:

  • when (ähnlich switch)
  • repeat-Schleifen und andere Schleifen

Tappe illustriert es am Emoji-Beispiel: Ist der Klickzähler 2, wird die „Thumbs up“-Funktion zweimal ausgeführt – und emittiert entsprechend zweimal UI.

Tooling und Bibliotheken: Refactoring, Suche, Material als Beispiel

Die Umstellung auf Compile-Time-Kotlin-UI eröffnet auch im Tooling Vorteile:

  • IDE-Unterstützung: „Refactoring und Suche sind großartig.“ Bekannte IntelliJ-Workflows greifen direkt im UI-Code.
  • Standardkomponenten als Lernmaterial: „Die ganze Material Library … ist in Compose selbst geschrieben.“ Damit sind die Komponenten gleichzeitig Dokumentation und Beispiel. Es sind keine opaque, tausende Zeilen fremder Magie; vielmehr nachvollziehbare Compose-Bausteine.

Interoperabilität: Schrittweise Migration ist möglich

Tappe adressiert die wichtigste Strategiefrage in bestehenden Apps: „Wie migrieren wir?“ Die Antwort: Interop ist Teil des Designs.

  • In einer bestehenden XML-Welt kann ein einzelnes Widget „ComposeView“ genutzt werden, um Composables einzubetten. Das öffnet den Weg, neue Teile deklarativ zu bauen, ohne die gesamte App sofort umzustellen.
  • Umgekehrt kann eine Compose-Funktion intern XML inflaten. Das erlaubt die Weiterverwendung bestehender Komponenten im neuen System.

Fazit: Migration „little by little“ ist machbar – ein Hybrid-Setup ist ausdrücklich vorgesehen.

Status und Reifegrad: Noch Beta, eng gekoppelt – mit spürbaren Auswirkungen

So positiv der Eindruck in Lernkurven und Architektur ist – Tappe benennt klar die operative Realität zum Zeitpunkt seines Vortrags: Compose sei „noch in Beta“. Zudem gebe es eine „enge Kopplung“ zwischen Compose, dem Compose-Compiler, dem Kotlin-Compiler und der Android-Studio-Preview. Der Hintergrund: Diese Bausteine mussten parallel entwickelt werden.

Die Folgen zeigen sich in konkreten Versionserfahrungen:

  • „In Compose Beta 6 funktionierte es nicht, in 7 auch nicht, in 8 schnell gefixt.“
  • Beim Test in der Haupt-App: „Unsere Main-App kompilierte in einer spezifischen Version nicht – zufällig genau die, die an die Compose-Version gekoppelt war, die ich für mein Beispiel brauchte.“
  • „Neue Versionen sind erschienen … aktuell funktioniert es.“ Dennoch bleibt die Einschätzung: „Ein gewisser Build-Instabilitätsgrad, auf den ich in einem Produktivsystem nicht bauen würde.“

Diese Ehrlichkeit ist zentral: Compose ist vielversprechend – aber Produktivteams brauchen planbare Ketten aus IDE, Compiler und Runtime. Kopplung macht Abhängigkeiten spürbar.

Adoptionsstrategie bei hi.health: Vorbereiten, integrieren, warten bis stabil

Auf die Frage „Adoptieren wir Compose?“ antwortet Tappe klar: „Ja, natürlich – beziehungsweise wir bereiten die App bereits darauf vor.“ Der Plan ist pragmatisch:

  • Neue Features mit Blick auf spätere Migration designen.
  • Interoperabilität nutzen, um schrittweise umzubauen.
  • Warten, „bis es stabil ist“ – „hoffentlich später in diesem Jahr“.

Der Impuls ist eindeutig: Die Richtung stimmt, der Zeitpunkt für Vollgas hängt vom Reifegrad des Tooling ab.

Was wir als Engineering-Publikum mitnehmen

Aus unserer Perspektive verdichten sich die Lernpunkte zu einer klaren Agenda:

  • Deklarativ statt imperativ: „UI als Funktion von Zustand“ vereinfacht mentale Modelle. Keine manuelle Synchronisation doppelter Zustände, weniger Lifecycle-Fallen.
  • Eine Sprache, ein Kontext: Vollständig in Kotlin zu arbeiten, vermeidet Kontextwechsel zwischen XML-Layout und Code.
  • Echte Wiederverwendung: Funktionen sind Komponenten; „Extract Method“ reicht oft, um Bausteine zu modularisieren.
  • Performance durch Runtime-Intelligenz: Das Compose-Compiler-/Runtime-Duo sorgt dafür, dass unveränderte Teile nicht neu ausgeführt werden müssen.
  • Migration im Bestand: ComposeView bzw. Inflating aus Compose heraus ermöglichen realistische, schrittweise Umstiege.
  • Tooling-Reife beachten: Solange enge Kopplungen bestehen und die Pipeline noch in Beta ist, muss man mit Versionskanten rechnen.

Die „Hi Clickable“-Demonstration als Blaupause

Auch wenn Tappe kein komplettes Code-Listing zeigte, war die Semantik klar: Ein kleiner, interaktiver Baustein mit Logo, Text und Klickzähler, der Emojis „hochzählt“ – und ab fünf „Thumbs up“ einen Stern zeigt. Dahinter steckt der Kern des deklarativen Ansatzes:

  • Zustandsänderung (Klick zählt hoch) triggert Re-Execution des Funktionsbaums.
  • Kontrollfluss in Kotlin entscheidet, ob Daumen oder Stern emittiert wird.
  • Wiederholung (z. B. mittels repeat) emittiert mehrfach UI („zwei Klicks – zwei Daumen“).

Die „Kleinheit“ des Beispiels ist Absicht: Wer das Prinzip beim Mini-Baustein versteht, kann es auf größere Oberflächen übertragen.

Konsequenzen für Teams und Codebasen

Unser Eindruck: Der Wechsel zu Compose ist nicht nur ein API-Update. Er betrifft Architektur, Team-Workflows und Testbarkeit. Ausgehend von Tappes Punkten lassen sich praktikable Richtungen ableiten – ohne über die Aussagen des Talks hinauszugehen:

  • UI-nahe Zustände lokal halten: Dinge wie Scroll-Position gehören in den UI-Baum, nicht ins Domänenmodell. Das reduziert Kopplung und macht Business-Logik schlanker.
  • Domänenzustand klar definieren: Je klarer, desto einfacher ist „UI als Funktion von Zustand“ zu leben.
  • Versionsdisziplin: Wer früh adoptiert, muss Versionen von Compose, Kotlin-Compiler und Android-Studio-Preview bewusst koordinieren – inklusive der Gefahr, in einzelne problematische Kombinationen zu laufen.
  • Schrittweise Migration als Default: Interop ist nicht nur Komfort, sondern Strategie. Einzelne Screens oder Komponenten können in Compose entstehen, während der Rest auf XML bleibt – so lange, wie es das Team braucht.

Ein Wort zum Design und zur Entwickler-Erfahrung

Tappe unterstreicht, wie angenehm sich das Arbeiten anfühlt: „Code-centric, yay, no context switches.“ Das Skopen von Funktionsaufrufen, die Natürlichkeit der Syntax, die IDE-Unterstützung für Refactoring und Suche – all das sorgt für ein reibungsloseres Arbeiten. Er formuliert es augenzwinkernd: Er hoffe, damit „sehr bald auch den Designer glücklich zu machen“.

Die Material-Komponenten, in Compose selbst geschrieben, sind dabei nicht nur Bausteine, sondern Lernmaterial. Wer verstehen will, „wie man es idiomatisch macht“, hat Beispiele in Form von produktionsnahen Komponenten.

Grenzen klar benennen: Beta heißt Beta

Die vielleicht wichtigste Führungsaussage in Tappes Vortrag ist die nüchterne Einschätzung des Status quo. Ja, Compose ist überzeugend – in Lernaufwand, Ausdrucksstärke und Architektur. Aber: „Noch Beta“, enge Kopplung und reale Beispiel-Erfahrungen mit fehlschlagenden Builds in konkreten Versionen. Genau deshalb ist das Adoptionsnarrativ bei hi.health so glaubwürdig: vorbereiten, integrieren, warten bis stabil.

Ressourcen und nächster Schritt

Zum Schluss richtet Tappe eine direkte Empfehlung an das Publikum: „Bitte schaut euch die Ressourcen an.“ Die „Talks von Google I/O“ und die „Dokumentation“ seien „wirklich großartig“. Wer das deklarative Paradigma verinnerlichen will, findet dort einen guten Einstieg.

Aus DevJobs.at-Sicht ist die Stoßrichtung eindeutig: Wer Android-UI heute plant oder umbaut, kommt an Compose nicht vorbei. Der Weg ist vorbereitet – Interoperabilität macht ihn gangbar. Die richtige Taktung hängt vom eigenen Risikoprofil und der Tooling-Stabilität ab.

Zusammenfassung: Unsere wichtigsten Takeaways

  • Compose verschiebt Android-UI in Richtung „UI als Funktion von Zustand“ – weniger Synchronisationsarbeit, klarere Architekturen.
  • Alles ist Kotlin: Kein XML, kein Kontextwechsel, stattdessen Funktionsbäume mit natürlicher Syntax (dank Lambdas und Trailing-Lambda-Syntax).
  • Runtime und Compiler-Plugin minimieren Re-Execution – deklarativer Code bleibt effizient.
  • Interoperabilität erlaubt inkrementelle Migration – ComposeView in XML und umgekehrt XML aus Compose heraus nutzen.
  • Der Status zum Zeitpunkt des Talks: „Noch Beta“, enge Kopplungen, reale Stolpersteine zwischen Compose-, Kotlin- und IDE-Versionen.
  • Adoptionsplan bei hi.health: Ja, aber schrittweise und mit Blick auf Stabilität „hoffentlich später dieses Jahr“.
  • Praktische Demo-Idee („Hi Clickable“): Kleine, interaktive Komponente, die die Prinzipien von State, Kontrollfluss und Emission verdeutlicht.

Wer die in „Declarative UI on Android“ von Adrian Tappe (hi.health) skizzierten Prinzipien aufnimmt, erhält eine klare Handreichung: Schrittweise umstellen, State in den Mittelpunkt rücken, Tooling im Blick behalten – und die Vorteile der deklarativen Arbeitsweise konsequent nutzen.

Weitere Tech Lead Stories