Technical
Managing TWA Lifecycle and Memory Limits on Android
August 30, 2026 · 8 min read
A common challenge when publishing a Progressive Web App (PWA) to Google Play using a Trusted Web Activity (TWA) is understanding how the underlying Android operating system manages system memory and the application lifecycle. Because a TWA relies on a decoupled web rendering engine, typically Chrome or another compatible browser engine running Custom Tabs, memory management operates differently compared to standard native applications or single-threaded WebView frameworks.
Understanding this architecture is essential for building a robust app that does not crash in the background or lose user progress when the OS reclaims resources.
The Architecture of TWA Processes
When a user opens your TWA app, Android launches your native application wrapper process. Simultaneously, the system binds to the custom tabs service of the device’s default browser, creating an isolated browser rendering process. This means your application essentially spans across two separate processes: the lightweight native Android shell and the main web browser engine execution thread.
Because the browser engine process handles the execution of JavaScript, CSS, and the Document Object Model (DOM), it consumes the majority of the application's memory footprint. However, because Android treats these as separate processes, the OS assigns priorities to each during system memory crunches. If the system experiences memory pressure, it may kill the browser renderer process or suspend the host Android activity, which can lead to app restarts or frozen user sessions.
How Android Manages Low Memory Situations
The Android operating system uses a driver mechanism called the Low Memory Killer (LMK) to monitor system RAM. When available RAM falls below specific thresholds, the LMK starts terminating processes based on their current priority state. Applications running in the background are terminated first, while the active foreground application is preserved whenever possible.
For a TWA, the priority configuration is unique. The table below illustrates how different app states correspond to Android process classifications and the relative risk of process termination:
| App State | Process Classification | LMK Termination Risk | Expected Behaviour |
|---|---|---|---|
| Foreground Active | Foreground Process | Very Low | The app runs normally; the browser rendering process runs at maximum priority. |
| Screen Locked | Visible Process | Low to Medium | The app is partially active; background rendering may continue but is throttled. |
| Background Minimised | Cached/Background Process | High | The OS can kill the browser process or the native shell at any time to free RAM. |
| Device Deep Sleep | Suspended Process | Extreme | The processes are frozen; if memory is needed, they are quietly terminated. |
Detecting and Reacting to Page Visibility
Because the system can suspend the TWA container when the user switches to a different application or locks their screen, you must use standard web APIs to detect these transitions. The Page Visibility API is the most reliable way to monitor these state shifts in real-time. By listening to the visibilitychange event, your JavaScript code can pause non-essential background tasks, halt media rendering, and save volatile states before the operating system halts execution.
Using this pattern prevents high CPU usage while the app is in the background, which reduces battery consumption and minimizes the likelihood of the LMK selecting your application for termination.
Implementing Visibility Listeners
To implement proactive state preservation, you can add a global event listener to your PWA application code:
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// The TWA has entered the background or the screen is locked
saveUserSessionState();
pauseHeavyAnimationsAndAPIPolling();
} else {
// The user has returned to the foreground
resumeHeavyTasks();
}
});
Preventing Out-of-Memory (OOM) Crashes
In addition to system-level process terminations, a TWA can crash if the browser renderer process runs out of memory allocated to individual web pages. This is known as a web page crash, and in a TWA, it manifests to the user as a blank screen, a frozen UI, or an immediate exit back to the Android home screen.
To avoid OOM crashes, pay close attention to resource allocation within your PWA. Common sources of memory leaks include:
- Unreleased Event Listeners: Frequently attaching event listeners to dynamic DOM elements without removing them when the elements are destroyed.
- Large Image Assets: Loading massive, unoptimised imagery that takes up vast amounts of graphics memory when decoded by Chromium.
- Retained DOM Trees: Keeping disconnected DOM nodes in JavaScript variables, preventing the browser garbage collector from reclaiming memory.
- Runaway Caching: Storing extensive dynamic data sets in memory or indexedDB without setting size limits or purging old entries.
Regularly profile your PWA using Chrome DevTools memory allocation tools on an Android device to find and patch these memory leaks before publishing updates.
Strategies for Robust State Recovery
Since you cannot completely prevent Android from killing your background TWA process to reclaim RAM, your architecture must be designed to recover gracefully. If a user receives a phone call, minimizes your app for ten minutes, and then returns, they should find themselves exactly where they left off, rather than on your start URL with an empty form.
Continuous State Persistency
Ensure that all critical application states are saved continuously to local storage engines. Do not wait for the visibilitychange event or the pagehide event, as these events may not always fire if the OS executes an abrupt termination. Save form data, active navigation views, and draft inputs to LocalStorage or IndexedDB as the user interacts with the application.
Validating Cached State on Reload
When the TWA reloads after a process termination, the application should inspect the persistence layer to see if a valid session state exists. Check the timestamp of the saved state; if the session occurred within a reasonable window, load the cached state and navigate the user back to their active route. This programmatic state recovery makes process termination completely transparent to the user, providing a true native-app experience.
Optimising Service Worker Memory Usage
The Service Worker runs in a separate execution thread from the DOM window. It is designed to start and stop dynamically on demand. However, a bloated Service Worker can add memory pressure to the device. Avoid keeping persistent connections, like active WebSockets, open inside your Service Worker. Ensure all database transactions close properly when complete, and structure your offline caching strategies to only store essential assets rather than caching entire API responses indiscriminately.
Ready to ship your Android app?
Paste your PWA URL, get a signed APK and a Google Play ready AAB in minutes.
Build my app