Real-Time Android Apps with Kotlin, WebSockets & Flow
Real-time applications need data to move from the server to the Android device without requiring constant polling.
Examples include:
- chat
- live dashboards
- delivery tracking
- multiplayer features
- IoT monitoring
- notifications
A typical architecture is:
Server
|
WebSocket
|
Android WebSocket Client
|
Kotlin Flow
|
ViewModel
|
Jetpack Compose / Views
Why WebSockets?
Polling:
Client -> Request
Server -> Response
wait
Client -> Request
Server -> Response
WebSockets maintain a persistent connection:
Client <================> Server
bidirectional
This is suitable for continuous real-time updates.
WebSocket Libraries
On Android, you can use a WebSocket-capable networking library such as OkHttp.
Example dependency:
dependencies {
implementation("com.squareup.okhttp3:okhttp:<version>")
}
Use the current compatible version for your project.
Create a WebSocket Client
class RealtimeClient(
private val client: OkHttpClient,
private val url: String
) {
fun connect(listener: WebSocketListener): WebSocket {
val request = Request.Builder()
.url(url)
.build()
return client.newWebSocket(request, listener)
}
}
Receive Messages
class RealtimeListener : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response
) {
println("WebSocket connected")
}
override fun onMessage(
webSocket: WebSocket,
text: String
) {
println("Message: $text")
}
override fun onFailure(
webSocket: WebSocket,
t: Throwable,
response: Response?
) {
println("WebSocket error: ${t.message}")
}
}
In production, do not put application business logic directly in the listener.
Convert Messages to Flow
Kotlin Flow provides a clean way to expose asynchronous events.
class RealtimeRepository {
private val _messages = MutableSharedFlow<String>(
extraBufferCapacity = 64
)
val messages: SharedFlow<String> = _messages.asSharedFlow()
fun onMessage(text: String) {
_messages.tryEmit(text)
}
}
Now the rest of the application can collect messages using Flow.
ViewModel
class ChatViewModel(
private val repository: RealtimeRepository
) : ViewModel() {
val messages = repository.messages
.map { text -> ChatMessage(text) }
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
emptyList()
)
}
The exact state transformation will depend on your application's requirements.
Lifecycle-Aware Collection
With Jetpack Compose, collect state using lifecycle-aware APIs where appropriate.
Conceptually:
@Composable
fun ChatScreen(viewModel: ChatViewModel) {
val messages by viewModel.messages.collectAsStateWithLifecycle()
// Render messages.
}
This helps avoid unnecessary collection when the UI is not active.
Sending Messages
fun send(webSocket: WebSocket, message: String) {
webSocket.send(message)
}
For structured messages, serialize a data class:
@Serializable
data class ChatRequest(
val type: String,
val message: String
)
Then encode it as JSON before sending.
Reconnection
Mobile connections frequently disappear.
Use a strategy such as:
Connected
|
Connection lost
|
Wait
|
Reconnect
|
Connected
Exponential backoff is preferable to reconnecting in a tight loop.
Connection State
Expose connection state explicitly:
sealed interface ConnectionState {
data object Disconnected : ConnectionState
data object Connecting : ConnectionState
data object Connected : ConnectionState
data class Error(val message: String) : ConnectionState
}
This allows the UI to show meaningful status.
Heartbeats
Some servers or network infrastructure close idle connections.
A heartbeat mechanism can help detect stale connections.
For example:
Client -> ping
Server -> pong
Use the protocol or server framework's recommended heartbeat behavior.
Threading
WebSocket callbacks should not perform expensive work directly.
A clean pipeline is:
WebSocket callback
↓
Repository
↓
Flow
↓
ViewModel
↓
UI
Keep CPU-heavy processing off the main thread.
Security
Use secure WebSockets in production:
wss://example.com/socket
Authenticate connections and validate all messages received from the server.
Conclusion
WebSockets provide the transport layer, while Kotlin Flow provides a powerful way to expose real-time events to the rest of an Android application.
Keeping the WebSocket client, repository, ViewModel, and UI responsibilities separate makes real-time Android applications easier to test and maintain.
Useful Links
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)