WebSockets (OkHttp)
Every networking lesson so far – OkHttp, Retrofit, Volley – has been built around one shape of interaction: the app sends a request, the server sends back a response, and the connection is done. That model works well for fetching a recipe list or submitting a new recipe. It breaks down for anything that needs to feel live.
Why Plain HTTP Isn't Enough
An HTTP request is a one-off round trip: open a connection, send a request, get a response, close the connection. If the server has new data a second later, your app has no way to know unless it asks again – typically by polling repeatedly, which is wasteful and still not instant. A WebSocket instead opens a single connection that both sides keep alive, and either side can send a message on it at any time.
HTTP
Request → Response → connection closes. Repeat to get new data.
WebSocket
One connection stays open. Either side sends messages anytime.
A RecipeBox Use Case: Cooking Together
Imagine a "cooking together" feature in RecipeBox: two users open the same recipe and cook it side by side, each seeing which step the other has reached in real time. Polling the server every few seconds for "has my friend's step changed?" would be slow and wasteful. A WebSocket connection lets each user's step update push instantly to the other – exactly the kind of feature WebSockets exist for.
OkHttp's Built-in WebSocket Support
Good news: you don't need a new library. The same OkHttpClient from android-okhttp.php already supports WebSockets through client.newWebSocket().
// CookingSessionSocket.java
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("wss://api.recipebox.example.com/cooking-session/42")
.build();
WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
Log.d("CookingSession", "Connected");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
StepUpdate update = new Gson().fromJson(text, StepUpdate.class);
runOnUiThread(() -> showFriendProgress(update));
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(1000, null);
runOnUiThread(() -> showStatus("Session closed: " + reason));
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
runOnUiThread(() -> showStatus("Connection lost: " + t.getMessage()));
}
});
- onOpen() fires once the connection is established.
- onMessage() fires every time the server pushes a new message – here, a JSON StepUpdate parsed with Gson (see android-json-parsing.php).
- onClosing() is the server's signal that it is shutting the connection down gracefully.
- onFailure() fires on connection errors – no automatic retry is built in, so reconnect logic is your responsibility.
Sending a Message
Once onOpen() has fired, sending data over the open connection is a single call:
// When the local user advances a step
StepUpdate myUpdate = new StepUpdate("user_1", currentStepIndex);
String json = new Gson().toJson(myUpdate);
webSocket.send(json);
Posting Updates Back to the Main Thread
Just like with OkHttp's enqueue() callbacks and Retrofit's Callback, WebSocketListener methods run on a background thread. Java has no coroutine or Flow bridge to hop threads automatically, so every UI update from onMessage(), onClosing(), or onFailure() must be wrapped in runOnUiThread() exactly as shown above – the same manual pattern used throughout this networking section.
Closing the Connection
When the cooking session screen is closed, shut the WebSocket down cleanly:
@Override
protected void onDestroy() {
super.onDestroy();
webSocket.close(1000, "Activity destroyed");
}