Android Coden
Android 8 min lesen

OkHttp-Konzepte: Clients, Interceptors und Timeouts

OkHttp ist der HTTP-Motor vieler Android-Apps. Du lernst Clients, Interceptors und Timeouts praxisnah einzuordnen.

Viele Android-Apps sprechen mit HTTP-APIs, auch wenn du das im UI-Code kaum siehst. Unter Retrofit, Repository-Klassen, Synchronisierung und Offline-First-Strategien arbeitet häufig OkHttp. Für dich ist wichtig zu verstehen, welche Verantwortung dieser HTTP-Motor übernimmt: Er baut Requests, verwaltet Verbindungen, ruft Server auf, liefert Responses zurück und entscheidet bei technischen Problemen nicht automatisch, was fachlich richtig ist.

Was ist das?

OkHttp ist eine HTTP-Client-Bibliothek für Kotlin- und Java-Anwendungen. Im Android-Kontext ist sie oft die untere Netzwerkschicht, auf der höhere Werkzeuge wie Retrofit aufbauen. Wenn Retrofit beschreibt, welche API-Endpunkte es gibt, kümmert sich OkHttp um den tatsächlichen Transport: DNS-Auflösung, Verbindungsaufbau, TLS, Request-Header, Response-Body, Caching-Optionen, Wiederverwendung von Verbindungen und Abbruch bei Timeouts.

Das mentale Modell ist einfach: Deine App formuliert eine Anfrage, OkHttp transportiert sie, der Server antwortet, OkHttp gibt das Ergebnis an deine Datenebene zurück. Diese Datenebene sollte nach moderner Android-Architektur nicht direkt in Composables sitzen. Üblicher ist eine Struktur aus UI, ViewModel, Repository und Datenquellen. Das Repository entscheidet, ob Daten aus dem Netzwerk, aus lokaler Speicherung oder aus beidem kommen. OkHttp ist dabei nicht die Architektur selbst, sondern ein technisches Werkzeug innerhalb der Remote-Datenquelle.

Drei Begriffe musst du sauber trennen. Ein OkHttpClient ist die konfigurierte Instanz deines HTTP-Clients. Interceptors sind Bausteine, die Requests und Responses auf dem Weg durch die Pipeline betrachten oder verändern können. Timeouts begrenzen, wie lange bestimmte Netzwerkphasen dauern dürfen. Zusammen bestimmen diese Konzepte, ob deine Netzwerkkommunikation nachvollziehbar, robust und wartbar bleibt.

Das Thema ist wichtig, weil Netzwerkfehler in echten Apps normal sind. Nutzer wechseln zwischen WLAN und Mobilfunk, Server antworten langsam, Tokens laufen ab, Geräte gehen in Energiesparzustände, Verbindungen brechen ab. OkHttp nimmt dir viele Transportdetails ab, aber es ersetzt keine saubere Entscheidung darüber, welche Fehler du dem Nutzer zeigst, welche Daten du lokal zwischenspeicherst und welche Operationen später erneut versucht werden.

Wie funktioniert es?

Der zentrale Einstieg ist der OkHttpClient. Er ist absichtlich als wiederverwendbares Objekt gedacht. Du solltest ihn nicht bei jedem API-Aufruf neu bauen, weil damit Verbindungspools, Caches und gemeinsame Konfigurationen verloren gehen. In einer Android-App entsteht der Client meist über Dependency Injection, zum Beispiel mit Hilt, und wird dann an Retrofit oder eine eigene Remote-Datenquelle weitergereicht.

Ein Client kann verschiedene Einstellungen besitzen: Timeouts, Interceptors, Cache, Authenticator, Zertifikatskonfigurationen und Dispatcher-Regeln. Für Lernende ist zuerst wichtig, dass diese Einstellungen technische Regeln für alle Requests dieses Clients sind. Wenn du einen Auth-Header, eine App-Version oder einen User-Agent immer mitsenden willst, gehört diese Logik nicht in jede einzelne Repository-Methode. Sie gehört an eine zentrale Stelle, häufig in einen Interceptor.

Interceptors funktionieren wie eine Kette. Ein Interceptor bekommt einen Request, kann ihn lesen oder ändern, ruft chain.proceed(request) auf und erhält danach die Response. Auch die Response kann er lesen, auswerten oder ersetzen. Damit eignen sich Interceptors für Querschnittsaufgaben. Beispiele sind Logging im Debug-Build, das Ergänzen von Headern, Metriken, einfache Retry-Entscheidungen oder das Erkennen bestimmter HTTP-Statuscodes.

Dabei gibt es eine wichtige Grenze: Interceptors sind gut für technische Netzwerklogik, aber schlecht als Ort für Fachlogik. Wenn dein Server bei 404 sagt, dass ein Produkt nicht existiert, ist das eine fachliche Information. Ob daraus im UI ein leerer Zustand, eine Fehlermeldung oder ein Navigationsschritt wird, sollte nicht im OkHttp-Interceptor entschieden werden. Der Interceptor darf erfassen, dass eine Response den Status 404 hat. Die fachliche Interpretation gehört in die Daten- oder Domain-Schicht.

Timeouts sind ein weiterer Kernpunkt. OkHttp unterscheidet unter anderem Connect Timeout, Read Timeout, Write Timeout und Call Timeout. Das Connect Timeout begrenzt den Verbindungsaufbau. Das Read Timeout betrifft das Warten auf Daten vom Server. Das Write Timeout betrifft das Senden des Request-Bodys, zum Beispiel bei Uploads. Das Call Timeout begrenzt den gesamten Aufruf. Diese Werte sind nicht nur technische Zahlen. Sie beeinflussen direkt, wie lange Nutzer warten und wie deine App mit schlechter Verbindung umgeht.

In einer Offline-First-App ist OkHttp nur ein Teil der Antwort. Die Android-Guidance zur Datenebene betont, dass Repositories Datenquellen koordinieren. Offline-First bedeutet, dass die App bei fehlendem Netzwerk nicht sofort nutzlos wird. OkHttp meldet dir Netzwerkprobleme, aber deine Architektur entscheidet, ob du lokale Daten zeigst, eine Synchronisierung später wiederholst oder einen klaren Fehlerzustand darstellst. Deshalb solltest du OkHttp nicht isoliert betrachten, sondern als Baustein unterhalb deiner Datenstrategie.

In der Praxis

In einer typischen App definierst du einen OkHttpClient einmal und nutzt ihn für deine API-Schicht. Das folgende Beispiel zeigt eine kompakte Konfiguration mit Timeouts und einem Interceptor für gemeinsame Header. Es ist bewusst klein gehalten, damit du die Zuständigkeiten erkennst.

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import java.util.concurrent.TimeUnit

class AppHeaderInterceptor(
    private val tokenProvider: TokenProvider
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()

        val requestBuilder = originalRequest.newBuilder()
            .header("Accept", "application/json")
            .header("X-App-Platform", "android")

        tokenProvider.currentToken()?.let { token ->
            requestBuilder.header("Authorization", "Bearer $token")
        }

        return chain.proceed(requestBuilder.build())
    }
}

interface TokenProvider {
    fun currentToken(): String?
}

fun createHttpClient(tokenProvider: TokenProvider): OkHttpClient {
    return OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(20, TimeUnit.SECONDS)
        .writeTimeout(20, TimeUnit.SECONDS)
        .callTimeout(30, TimeUnit.SECONDS)
        .addInterceptor(AppHeaderInterceptor(tokenProvider))
        .build()
}

Der Client setzt hier klare Grenzen. Wenn der Verbindungsaufbau nach zehn Sekunden nicht klappt, soll der Aufruf fehlschlagen. Wenn der gesamte Call länger als dreißig Sekunden dauert, wird er ebenfalls beendet. Der Header-Interceptor sorgt dafür, dass jede Anfrage die gleichen Basisinformationen erhält. Das reduziert Wiederholung und verhindert, dass einzelne API-Methoden versehentlich ohne Authentifizierung oder falschen Accept-Header arbeiten.

In einer echten App würdest du diesen Client meist in einem DI-Modul bereitstellen und an Retrofit übergeben. Das Repository ruft dann nicht direkt OkHttp auf, sondern nutzt ein API-Interface oder eine Remote-Datenquelle. So bleibt dein ViewModel frei von Transportdetails. Für Jetpack Compose bedeutet das: Deine Composables beobachten UI-State, aber sie wissen nicht, ob die Daten gerade per OkHttp, aus Room oder aus einem Cache kommen. Das macht die UI testbarer und die Netzwerklogik austauschbarer.

Eine praktische Entscheidungsregel lautet: Alles, was für jeden HTTP-Aufruf gleich ist und technisch zum Transport gehört, darf in den OkHttpClient oder in einen Interceptor. Alles, was von einem konkreten Use Case abhängt, gehört eher in Repository, DataSource oder Domain-Schicht. Ein Auth-Header ist oft Interceptor-Material. Die Entscheidung, ob nach einem fehlgeschlagenen Warenkorb-Request der lokale Warenkorb zurückgesetzt wird, ist keine Interceptor-Aufgabe.

Eine typische Stolperfalle ist das unkontrollierte Logging. Ein Logging-Interceptor ist beim Debuggen sehr hilfreich, kann aber sensible Daten ausgeben: Tokens, personenbezogene Informationen, Session-IDs oder vollständige Request-Bodys. In Release-Builds solltest du solche Logs deaktivieren oder stark begrenzen. Prüfe außerdem, ob du Header wie Authorization maskierst. Netzwerklogs sind nützlich, aber sie dürfen nicht zur Sicherheitslücke werden.

Eine zweite Stolperfalle ist das blinde Wiederholen von Requests. Technisch kannst du in Interceptors oder anderen Netzwerkkomponenten Retry-Logik bauen. Aber nicht jeder Request ist gefahrlos wiederholbar. Ein GET für eine Liste ist meist unkritischer als ein POST, der eine Bestellung auslöst. Wenn du Wiederholungen einbaust, musst du wissen, ob der Server idempotente Operationen unterstützt oder ob du doppelte Aktionen erzeugen könntest. Für viele Apps ist es besser, Retries bewusst in der Daten- oder Synchronisationslogik zu behandeln.

Auch Timeouts werden häufig falsch verstanden. Sehr lange Timeouts wirken zunächst komfortabel, weil weniger Fehler sichtbar werden. Für Nutzer kann das aber bedeuten, dass die App lange in einem Ladezustand hängt. Sehr kurze Timeouts erzeugen dagegen unnötige Fehler bei schwacher Verbindung. Wähle Werte passend zum Vorgang. Ein kleiner JSON-Request braucht andere Grenzen als ein Bild-Upload. In Code-Reviews solltest du nachfragen, warum konkrete Timeout-Werte gewählt wurden und ob sie zum Produktverhalten passen.

Zum Testen kannst du OkHttp gut von außen beobachten. Mit MockWebServer lässt sich ein Server simulieren, der bestimmte Responses, Header, Statuscodes oder Verzögerungen liefert. Damit kannst du prüfen, ob dein Client Header korrekt setzt, ob Timeouts greifen und ob deine Datenebene Fehler sauber weitergibt. Zusätzlich hilft der Debugger: Setze Breakpoints im Interceptor und beobachte, welche URL, Methode, Header und Response-Codes wirklich durchlaufen. So lernst du schnell, ob dein mentales Modell zur Laufzeit stimmt.

Für Offline-First ist ein weiterer Praxispunkt wichtig: Behandle OkHttp-Fehler nicht automatisch als UI-Katastrophe. Ein IOException kann bedeuten, dass kein Netzwerk verfügbar ist. Deine App kann trotzdem lokale Daten anzeigen und eine Synchronisierung später erneut versuchen. Das gehört aber nicht allein in den HTTP-Client. Der Client meldet den technischen Fehler. Repository und Synchronisationslogik entscheiden, welche lokale Quelle genutzt wird und welcher Zustand an das ViewModel geht.

Fazit

OkHttp ist der HTTP-Motor unter vielen Android-Netzwerkstacks. Wenn du Clients, Interceptors und Timeouts verstehst, kannst du API-Kommunikation klarer strukturieren, Fehler besser einordnen und deine Datenebene sauber von der UI trennen. Prüfe dein Wissen praktisch: Baue einen kleinen Client mit Header-Interceptor, simuliere langsame Antworten mit einem Testserver, beobachte die Requests im Debugger und lass im Code-Review bewusst erklären, welche Logik im Interceptor liegt und welche in Repository oder Domain-Schicht gehört.

Quellen (2)
Redaktion

Geschrieben von

Redaktion

Das Redaktionsteam recherchiert und schreibt Artikel zu aktuellen Themen rund um Tech, Lifestyle und Ratgeber.