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:

  1. Foreground: WebSocket/SSE for active screens
  2. Background: FCM to trigger data sync via WorkManager
  3. 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

codeTap to expand ⛶
1// Real-time update architecture using SSE + FCM fallback
2
3// SSE client for live feed updates
4class LiveFeedConnection @Inject constructor(
5 private val okHttpClient: OkHttpClient
6) {
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()
12
13 val call = okHttpClient.newCall(request)
14 val response = call.execute()
15 val source = response.body?.source() ?: throw IOException("Empty body")
16
17 launch(Dispatchers.IO) {
18 try {
19 while (!source.exhausted()) {
20 val line = source.readUtf8Line() ?: continue
21 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 }
31
32 awaitClose {
33 call.cancel()
34 response.close()
35 }
36 }
37}
38
39// Lifecycle-aware real-time connection
40class RealTimeManager @Inject constructor(
41 private val sseConnection: LiveFeedConnection,
42 private val feedDao: FeedDao,
43 private val processLifecycle: ProcessLifecycleOwner
44) {
45 private var connectionJob: Job? = null
46
47 fun observeFeed(feedId: String): Flow<List<FeedItem>> {
48 // Always return from local DB (source of truth)
49 return feedDao.getItems(feedId)
50 }
51
52 fun startListening(feedId: String, scope: CoroutineScope) {
53 connectionJob?.cancel()
54 connectionJob = scope.launch {
55 // Reconnect with backoff on failure
56 var delay = 1000L
57 while (isActive) {
58 try {
59 sseConnection.connect(feedId).collect { update ->
60 delay = 1000L // Reset backoff on success
61 feedDao.upsert(update.toEntity())
62 }
63 } catch (e: Exception) {
64 if (e is CancellationException) throw e
65 delay(delay)
66 delay = (delay * 2).coerceAtMost(30_000L) // Max 30s backoff
67 }
68 }
69 }
70 }
71
72 fun stopListening() {
73 connectionJob?.cancel()
74 }
75}

🏋️ Practice Exercise

Practice:

  1. Implement an SSE client that reconnects automatically with exponential backoff
  2. Design a live score update system using WebSocket + FCM fallback
  3. Compare battery impact of polling every 5s vs SSE vs WebSocket
  4. Implement adaptive polling — faster when user interacts, slower when idle
  5. 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.