WebView-Status effizient verwalten

Bei der Verwaltung des Lebenszyklus einer Android-App ist das Beibehalten des Nutzerstatus während der Hintergrundressourcenfreigabe ein wichtiger Bestandteil einer nahtlosen Nutzererfahrung. Bei Apps mit Web-Workflows können Sie mit WebView.saveState(Bundle) den Navigationsverlauf und den Status eines WebView in ein Bundle serialisieren. Diese Daten können anschließend mit WebView.restoreState(Bundle) wiederhergestellt werden.

Bei Standardimplementierungen können jedoch bei intensiven Browsersitzungen Einschränkungen hinsichtlich der Transaktionsgröße auftreten. Auf dieser Seite werden diese architektonischen Einschränkungen beschrieben und Strategien vorgestellt, mit denen speicherbezogene Ausnahmen vermieden werden können, während der Navigationsverlauf beibehalten wird.

Transaktionslimit von 1 MB und Statuslöschung

Android legt ein strenges Limit von 1 MB für das Gesamtvolumen der Daten fest, die in savedInstanceState gespeichert werden können. Dieses 1-MB-Budget wird für den gesamten App-Prozess verwendet. Wenn eine App mehrere WebView-Instanzen enthält, müssen der gemeinsame Navigationsstatus und der Verlauf in diese einzelne gemeinsame Zuweisung passen. Das Überschreiten dieser Grenze löst eine TransactionTooLargeException aus, was zu einem App-Absturz führt.

Eine gängige, aber problematische Strategie zur Risikominderung besteht darin, die Größe des WebView-Status-Bundle zu überwachen und den WebView-Verlauf vollständig zu löschen, wenn er eine beliebige Sicherheitsschwelle (z. B. 300 KB) überschreitet. Dadurch wird zwar ein Absturz verhindert, aber es kommt zu erheblichen Rückschritten bei der Nutzererfahrung:

  • Verlust der Rückwärtsnavigation: Android beendet häufig Hintergrund-App Prozesse, um Speicher für andere Aufgaben freizugeben. Sie können saveState(Bundle) im Lebenszyklus-Callback onSaveInstanceState() verwenden, um den Navigationsverlauf beizubehalten. Wenn Sie diesen Verlauf löschen, um das Transaktionslimit von 1 MB zu vermeiden, geht der gesamte Navigationsstack verloren. Wenn der Nutzer zur App zurückkehrt, wird die Komponente oder App sofort über den Button „Zurück“ des Systems beendet, da kein Verlaufskontext mehr vorhanden ist, der die Rückwärtsnavigation unterstützt. Dies gilt unabhängig davon, ob ein Prozessneustart stattgefunden hat.

  • BFCache-Ungültigmachung: Wenn Sie den Verlauf löschen, kann die App den Back-Forward-Cache (BFCache) nicht verwenden. Dadurch können zuvor besuchte Seiten nicht sofort gerendert werden.

  • Erhöhte Latenz: Nutzer verlieren ihren aktuellen Status im WebView, und müssen die Navigation und Initialisierung vollständig neu durchführen. Dieser Prozess erhöht den Netzwerk-Overhead und die Transaktionslatenz erheblich.

Architektonische Strategien zur Risikominderung

Um Abstürze aufgrund von TransactionTooLargeException zu vermeiden, ohne die Nutzererfahrung durch das vollständige Löschen des Verlaufs zu beeinträchtigen, müssen Sie ein strenges Gleichgewicht zwischen dem Beibehalten des Status und der Speichereffizienz wahren. Durch die Implementierung der folgenden Optimierungsstrategien können Sie das Transaktionsbudget von 1 MB sicher verwalten und gleichzeitig den wichtigen Navigationsverlauf und die Sitzungsintegrität beibehalten.

Größenlimits für die Statusserialisierung erzwingen

Anstatt den Navigationsstack vollständig zu löschen, wenn er zu groß wird, ist es effektiver, die Verlaufsdaten zu kürzen:

  • Gezielte Löschrichtlinie: Verwenden Sie WebViewCompat.saveState(), um den Status zu serialisieren und gleichzeitig ein bestimmtes Byte-Limit zu erzwingen (z. B. WebViewCompat.saveState(webView, outState, maxSizeBytes)). Mit dieser API werden ältere Navigationseinträge automatisch sequenziell gelöscht, bis die Gesamt-Nutzlast in die von Ihnen definierte Zuweisung passt. Wichtig ist, dass dadurch nur das serialisierte Bundle gekürzt wird, ohne den Live-Verlauf des aktiven WebView zu ändern oder zu löschen. Die sofortige Rückwärtsnavigation bleibt also vollständig erhalten.

  • Entfernen von Vorwärtseinträgen: Wenn die Anwendungsoberfläche einen Button „Zurück“ bietet aber keinen speziellen Button „Weiter“, können Sie alle Vorwärtsnavigationseinträge verwerfen, indem Sie den saveState API-Parameter includeForwardState auf false setzen. Dadurch wird die Nutzlastgröße erheblich reduziert, ohne die verfügbaren Navigationspfade des Nutzers zu beeinträchtigen.

Ressourcenlatenz mit der HTTP Cache Quota API verwalten

Während saveState das Limit von 1 MB für das Bundle für den kurzlebigen Navigationsverlauf verwaltet, bietet die HTTP Cache Quota API eine manuelle Steuerung der persistenten Webressourcen (Datenträger-Cache) pro Profil. Dadurch wird klar zwischen dem kurzfristigen Navigationskontext und den langfristig im Cache gespeicherten Assets unterschieden.

Bei der Auswahl eines geeigneten Kontingents müssen Sie einen Kompromiss zwischen Leistung und Speicherplatz eingehen:

  • Höhere Kontingente verbessern die Offlineverfügbarkeit und die Latenz beim Laden von Ressourcen, da mehr Assets auf der Festplatte gespeichert werden.
  • Niedrigere Kontingente minimieren den Speicherbedarf der App auf der Festplatte und verhindern, dass andere wichtige App-Daten vom Betriebssystem aus dem Cache entfernt werden.

Diese Einstellungen bleiben auch nach einem Neustart der App erhalten und müssen im Hauptthread konfiguriert werden.

Die folgende Implementierung zeigt, wie Sie ein Datenträger-Cache-Kontingent für das Standardprofil konfigurieren:

Kotlin

if (WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE) &&
    WebViewFeature.isFeatureSupported(WebViewFeature.HTTP_CACHE)) {
    val defaultProfile = ProfileStore.getInstance()
        .getOrCreateProfile(Profile.DEFAULT_PROFILE_NAME)
    val httpCache = defaultProfile.httpCache

    // Set explicit cache size to 50MB (50 * 1024 * 1024 bytes)
    httpCache.setQuotaBytes(50L * 1024 * 1024)
}

Java

if (WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE) &&
    WebViewFeature.isFeatureSupported(WebViewFeature.HTTP_CACHE)) {
    Profile defaultProfile = ProfileStore.getInstance()
        .getOrCreateProfile(Profile.DEFAULT_PROFILE_NAME);
    HttpCache httpCache = defaultProfile.getHttpCache();

    // Set explicit cache size to 50MB (50 * 1024 * 1024 bytes)
    httpCache.setQuotaBytes(50L * 1024 * 1024);
}

Weitere Informationen zu Strategien für die Kontingentgröße, zur Lebenszyklusverwaltung und zu Profilgrenzen finden Sie unter HTTP-Cache-Kontingent in WebView verwalten.

Wichtige Überlegungen zur Leistung

In den folgenden Punkten werden die technischen Einschränkungen und das interne Datenverhalten hervorgehoben, die das WebView-Statusverhalten bestimmen:

  • Opake PageState Blobs: Etwa 70% der von saveState gespeicherten Daten bestehen aus internen PageState Blobs aus der Rendering-Engine. Diese Daten erfassen detaillierte Sitzungsstatus, einschließlich Formulareingaben und Scrollpositionen von iFrames. Versuchen Sie nicht, einzelne Segmente aus diesen Blobs manuell zu parsen oder zu entfernen, da dies erhebliche Sicherheitsrisiken birgt und die Integrität der Sitzungswiederherstellung beeinträchtigt.

  • Granulare Verlaufsverwaltung: Die Standard-API WebBackForwardList unterstützt nicht das beliebige Entfernen einzelner Verlaufselemente. Für eine strenge Statusverwaltung müssen Sie Kürzungsmethoden mit den Parametern maxSizeBytes und includeForwardState in WebViewCompat.saveState() implementieren, um die architektonische Sicherheit zu gewährleisten.