Technical
Using the Background Fetch API in Android TWAs
October 12, 2026 · 8 min read
A common limitation of standard web applications running in mobile environments is their susceptibility to network interruptions and process termination. When a user initiates a large file download or upload within a standard browser tab, navigating away from the page or turning off the screen often causes the browser engine to suspend or terminate the active transfer. This limitation historically forced developers to build fully native apps for media-heavy or data-intensive workflows.
For developers converting their Progressive Web Apps (PWAs) into Android apps via a Trusted Web Activity (TWA), the Background Fetch API offers a robust alternative. This API enables your application to hand off large file uploads and downloads directly to the underlying Chromium engine. The operating system manages the transfer in the background, complete with system-level progress notifications, even if the user closes the TWA wrapper completely or loses network connectivity entirely.
How Background Fetch Integrates with Android
In a standard web environment, a web page manages its network requests inside a temporary container. If that container is closed, the request dies. The Background Fetch API solves this by transferring control of the network request to a service worker registration.
When your TWA initiates a background fetch, Chrome registers the operation with the Android system download manager. Android presents a persistent notification in the drawer, displaying the name of your application, the upload or download progress, and a cancellation button. If the operating system runs low on memory and closes your TWA process, the background download continues uninterrupted. Once the transfer completes, the system wakes up your service worker to handle the final routing of the files.
Contrasting Synchronization APIs
Choosing the correct API for network tasks prevents battery drain and unnecessary complexity. The table below displays how Background Fetch compares to other offline-focused APIs available inside a Trusted Web Activity.
| API Name | Primary Use Case | File Size Limit | User-Visible Notification | Background Operation Lifecycle |
|---|---|---|---|---|
| Standard Fetch | Short HTTP requests, API calls | Small (dependent on RAM) | No system UI | Terminates immediately when app is closed |
| Background Sync | Syncing form data, messaging | Small (text-based payloads) | No system UI | Runs briefly when network connectivity returns |
| Periodic Background Sync | Pre-fetching news, feed updates | Moderate | No system UI | Runs periodically based on Android OS heuristic scores |
| Background Fetch | Large uploads and downloads | Large (gigabyte scale) | Yes (Android System Tray) | Runs to completion managed by OS download manager |
Registering a Background Fetch
To use the Background Fetch API, your service worker must be registered and active. The registration process begins in your frontend web application code by calling the backgroundFetch.fetch() method on the active service worker registration object. This registration requires a unique identifier, an array of resource URLs, and an options configuration object.
The following example code illustrates how to trigger a background fetch operation for downloading media assets from your application interface:
async function triggerAssetDownload() {
const registration = await navigator.serviceWorker.ready;
try {
const fetchRegistration = await registration.backgroundFetch.fetch("movie-download-id", [
"/assets/video/movie.mp4",
"/assets/video/poster.jpg"
], {
title: "Downloading Offline Movie Pack",
icons: [{ src: "/icons/icon-192x192.png", sizes: "192x192", type: "image/png" }],
downloadTotal: 250 * 1024 * 1024
});
fetchRegistration.addEventListener("progress", () => {
let percentage = Math.round((fetchRegistration.downloaded / fetchRegistration.downloadTotal) * 100);
console.log("Download progress: " + percentage + "%");
});
} catch (error) {
console.error("Background Fetch initialization failed:", error);
}
}
The downloadTotal property is highly recommended. It informs the underlying Android system of the total expected footprint of the transfer, allowing the browser to warn the user if they do not have sufficient storage space before the download begins.
Handling Events in the Service Worker
When the background fetch completes, fails, or is cancelled by the user, the operating system triggers a functional event inside your service worker. The service worker must listen to these events to store the downloaded resources in the Cache Storage or notify the user of the final result.
You must implement listeners for backgroundfetchsuccess and backgroundfetchfail inside your service worker file, as illustrated below:
self.addEventListener("backgroundfetchsuccess", (event) => {
event.waitUntil(async function() {
const cache = await caches.open("offline-media-v1");
const records = await event.registration.matchAll();
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
await Promise.all(promises);
await event.updateUI({ title: "Movie Pack Ready Offline!" });
}());
});
self.addEventListener("backgroundfetchfail", (event) => {
event.waitUntil(async function() {
console.error("Background fetch transaction failed:", event.registration.id);
}());
});
Inside the backgroundfetchsuccess handler, calling event.registration.matchAll() retrieves the list of transfer records. The service worker extracts the responses and places them into the standard browser cache. This ensures that the next time the user requests these files within your TWA, your app serves them locally, instantly, and without consuming mobile data.
Managing System Interruptions and Retries
Android devices constantly monitor battery usage and network states. If a user transitions from a Wi-Fi connection to mobile data while a background fetch is running, the operating system may pause the download depending on the user's system preferences. The Background Fetch API manages this automatic pause-and-resume behavior natively, ensuring your app does not consume metered cellular data unless specifically permitted.
If a hard network failure occurs, the browser engine will attempt to retry the fetch over a specified duration. If the transfer fails completely, the backgroundfetchfail event fires. This is your opportunity to clean up partial cache allocations or alert the user via local web notifications once they open the app again.
Policy Requirements and Android System Quotas
While the Background Fetch API is highly effective, Google Play Store policies and Android operating system constraints require developers to implement this feature responsibly. Because downloads generate native system notifications, abusive or spam-like background requests can result in users disabling notifications for your app entirely, or worse, trigger programmatic app flags for excessive resource usage.
Ensure your storage configurations respect device limitations. If your TWA attempts to download files that exceed the storage quota allocated to Chrome by the Android operating system, the request will fail. To prevent this, always query the storage manager before registering a fetch:
if (navigator.storage && navigator.storage.estimate) {
const estimate = await navigator.storage.estimate();
const availableSpace = estimate.quota - estimate.usage;
if (availableSpace < REQUIRED_BYTES) {
alert("Insufficient device storage to complete background download.");
}
}
By verifying both network state and available system storage, your TWA application can leverage the same background performance capabilities as a traditional native Android application.
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