Designing Real-Time Update Systems
📖 Concept
Real-time updates in mobile apps require careful architectural decisions about when to use WebSockets, Server-Sent Events (SSE), long polling, or push notifications.
Communication protocols comparison:
| Protocol | Direction | Latency | Battery | Use Case |
|---|---|---|---|---|
| HTTP Polling | Client→Server | High (interval) | Bad | Legacy, simple |
| Long Polling | Client↔Server | Medium | Medium | Moderate real-time |
| SSE | Server→Client | Low | Good | Live feeds, notifications |
| WebSocket | Bidirectional | Very low | Medium | Chat, collaboration |
| FCM/Push | Server→Client | Variable | Best | Background notifications |
When to use what:
- WebSocket: Bidirectional real-time (chat, multiplayer games, live collaboration)
- SSE: Server-to-client streaming (live scores, stock tickers, feed updates)
- FCM: Background-safe notifications (email alerts, social notifications)
- Polling: When WebSocket/SSE infrastructure isn't available
Architecture patterns for real-time Android apps:
- Foreground: WebSocket/SSE for active screens
- Background: FCM to trigger data sync via WorkManager
- Fallback: Polling with adaptive interval (more frequent when active, less when idle)
Handling connection lifecycle:
- Connect WebSocket when app is foregrounded
- Disconnect when backgrounded (save battery)
- FCM handles background delivery
- On foreground resume, fetch missed messages via REST API
💻 Code Example
1// Real-time update architecture using SSE + FCM fallback23// SSE client for live feed updates4class LiveFeedConnection @Inject constructor(5 private val okHttpClient: OkHttpClient6) {7 fun connect(feedId: String): Flow<FeedUpdate> = callbackFlow {8 val request = Request.Builder()9 .url("https://api.example.com/feed/$feedId/stream")10 .header("Accept", "text/event-stream")11 .build()1213 val call = okHttpClient.newCall(request)14 val response = call.execute()15 val source = response.body?.source() ?: throw IOException("Empty body")1617 launch(Dispatchers.IO) {18 try {19 while (!source.exhausted()) {20 val line = source.readUtf8Line() ?: continue21 if (line.startsWith("data:")) {22 val json = line.removePrefix("data:").trim()23 val update = Json.decodeFromString<FeedUpdate>(json)24 trySend(update)25 }26 }27 } catch (e: IOException) {28 if (!isClosedForSend) close(e)29 }30 }3132 awaitClose {33 call.cancel()34 response.close()35 }36 }37}3839// Lifecycle-aware real-time connection40class RealTimeManager @Inject constructor(41 private val sseConnection: LiveFeedConnection,42 private val feedDao: FeedDao,43 private val processLifecycle: ProcessLifecycleOwner44) {45 private var connectionJob: Job? = null4647 fun observeFeed(feedId: String): Flow<List<FeedItem>> {48 // Always return from local DB (source of truth)49 return feedDao.getItems(feedId)50 }5152 fun startListening(feedId: String, scope: CoroutineScope) {53 connectionJob?.cancel()54 connectionJob = scope.launch {55 // Reconnect with backoff on failure56 var delay = 1000L57 while (isActive) {58 try {59 sseConnection.connect(feedId).collect { update ->60 delay = 1000L // Reset backoff on success61 feedDao.upsert(update.toEntity())62 }63 } catch (e: Exception) {64 if (e is CancellationException) throw e65 delay(delay)66 delay = (delay * 2).coerceAtMost(30_000L) // Max 30s backoff67 }68 }69 }70 }7172 fun stopListening() {73 connectionJob?.cancel()74 }75}
🏋️ Practice Exercise
Practice:
- Implement an SSE client that reconnects automatically with exponential backoff
- Design a live score update system using WebSocket + FCM fallback
- Compare battery impact of polling every 5s vs SSE vs WebSocket
- Implement adaptive polling — faster when user interacts, slower when idle
- Design a real-time presence system (show who's online in a chat app)
⚠️ Common Mistakes
Keeping WebSocket connections open in background — drains battery, gets killed by OS
Using polling when SSE would work — SSE is more efficient for server-to-client streaming
Not implementing reconnection logic — mobile networks are unreliable, always reconnect
Fetching full data on reconnect instead of delta — wastes bandwidth after brief disconnections
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Designing Real-Time Update Systems. Login to unlock this feature.