SPEAKER NOTES:
Today's demo extends Monday's coroutines with WorkManager. Coroutines run async work while the app is open. WorkManager handles work that must complete even if the app closes. Both are essential for real Android apps.
SPEAKER NOTES:
Start by establishing the problem WorkManager solves. Students understand viewModelScope from Monday — now let's show its limitation.
SPEAKER NOTES:
Ask: "What happens if the user presses Home while this upload is running?" Android may kill the process to reclaim memory. The coroutine disappears mid-flight. The upload never completes. The user doesn't know. This is the exact problem WorkManager was built to solve.
SPEAKER NOTES:
WorkManager persists work to a database before scheduling it. Even if the app is killed, Android will restart the worker when conditions are met. This is what "guaranteed delivery" means in practice.
SPEAKER NOTES:
Real pattern: coroutines handle immediate, foreground work (loading data, updating UI). WorkManager handles deferred, must-complete work (uploading logs, syncing to server). In a well-built app you'll use both.
SPEAKER NOTES:
Now let's look at the building blocks. Four pieces: Worker, WorkRequest, Constraints, WorkManager enqueue.
SPEAKER NOTES:
Always use the -ktx variant in Kotlin projects. Without it you get Worker (blocking, no coroutines). With it you get CoroutineWorker (suspendable, coroutine-native). There's no reason to use the non-ktx version in new projects.
SPEAKER NOTES:
CoroutineWorker.doWork() is a suspend function — you can call other suspend functions directly. It already runs on Dispatchers.Default, so you can also call withContext(IO) for network/database work inside it. Three return values: success (done), failure (give up), retry (try again after backoff).
SPEAKER NOTES:
failure() vs retry(): use failure() when the data is invalid or unrecoverable (e.g., the server rejected the payload). Use retry() for transient problems (network timeout, server 503). runAttemptCount lets you limit retries manually — 3 is a reasonable upper bound for most sync tasks.
SPEAKER NOTES:
Constraints delay work until conditions are met. The work stays in a queue (persisted to disk) until Android sees that all constraints pass. For uploading user data, CONNECTED is usually right — any connection is fine. UNMETERED (WiFi only) makes sense for large file downloads where you don't want to use the user's cellular data.
SPEAKER NOTES:
Exponential backoff is the right default for network retries — if a server is overloaded, hammering it with retries every 10 seconds makes things worse. Exponential backoff gives the server breathing room. EXPONENTIAL doubles the wait each time: 10s → 20s → 40s → 80s...
SPEAKER NOTES:
enqueueUniqueWork prevents duplicate workers. If the user submits 5 records while offline, you don't want 5 sync workers — you want ONE that picks up all 5. KEEP = "if sync is already waiting, don't add another." The worker then queries the database for all pending records, not just the one that triggered it.
SPEAKER NOTES:
Enqueueing work is only half the story. Users want to know if their data synced. WorkManager provides observable work status.
SPEAKER NOTES:
WorkInfo.State has six values: BLOCKED, ENQUEUED, RUNNING, SUCCEEDED, FAILED, CANCELLED. In practice you care about RUNNING (show a spinner), SUCCEEDED (confirm to user), FAILED (offer retry). ENQUEUED is often too noisy to surface in UI.
SPEAKER NOTES:
observeAsState() converts LiveData to Compose state. This small indicator (14dp spinner + label) can live in the app bar or bottom of a screen. Keep it subtle — users don't need a big status update unless something goes wrong. Hide it entirely when not syncing.
SPEAKER NOTES:
Workers often need input (which records to process) and sometimes produce output (how many records were synced). WorkManager has a lightweight data API for this.
SPEAKER NOTES:
inputData is a key-value store limited to ~10KB. For large data (lists of records), save to the database and pass just an ID. For small parameters (user ID, feature flags, filter options), pass directly. The data is serialized and stored by WorkManager so it survives process death.
SPEAKER NOTES:
Output data is available in WorkInfo after the worker succeeds. Same ~10KB limit applies. Use this for surfacing summary results to the user: "3 records synced," "Upload complete," etc. Don't try to pass large result sets — just pass counts or status codes.
SPEAKER NOTES:
Two more WorkManager features worth knowing: recurring work on a schedule, and multi-step work pipelines.
SPEAKER NOTES:
15 minutes is Android's hard floor — you cannot schedule work more frequently than this with WorkManager. For most sync scenarios this is appropriate. If you need more frequent updates, the user is actively using the app and you should use coroutines instead. PeriodicWorkRequest is for background maintenance: pruning old data, refreshing a cache, etc.
SPEAKER NOTES:
Chains let you model workflows with dependencies. Validate before you upload. Notify only after a successful upload. If validation fails, the upload never runs — no wasted network request. Data can flow through the chain using inputData/outputData, making this a powerful pipeline model.
SPEAKER NOTES:
Let's see how coroutines and WorkManager fit together in a ViewModel. This is the complete pattern for immediate + reliable background work.
SPEAKER NOTES:
Walk through the three steps:
1. Save locally first — the user gets confirmation immediately, even with no network
2. Update StateFlow — the UI reflects the save right away
3. Schedule WorkManager — the server will get the data when conditions allow
This is the offline-first pattern. Users experience instant feedback. The server sync is an implementation detail they don't have to wait for.
SPEAKER NOTES:
A private helper keeps the ViewModel clean. enqueueUniqueWork with KEEP is key: if sync is already queued waiting for WiFi, don't add another. The worker queries all unsynced records from the database, not just the one that triggered this call — so batch submissions are handled efficiently.
SPEAKER NOTES:
This diagram is worth drawing on the board. The key insight: the user's action and the server sync are decoupled. The user gets instant feedback. The server gets reliable data. Neither depends on the other being immediate.
SPEAKER NOTES:
Quick reference for the seven concepts. Friday's lab focuses on Retrofit + coroutines — the networking piece that feeds into the WorkManager sync pattern. The Design Document is due Sunday — the architecture section should describe how your project handles background sync.
SPEAKER NOTES:
Lab 7 uses a real NASA API (Mars rover photos). The pattern from Monday — delay() simulating a network call — becomes a real Retrofit suspend function. After Friday's lab, students have all the pieces: local data (Room next week), network (Retrofit), immediate async (coroutines), and reliable background (WorkManager).