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()));
    }
});
Explanation:
  • 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);
Explanation: webSocket.send(text) pushes the message immediately over the already-open connection – there is no new request/response cycle to wait on.

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");
}

Is It Worth the Complexity?

Honest note: WebSockets add real complexity that a simple GET/POST call does not have – you must manage the connection's lifecycle (open, keep-alive, close), decide what happens when the app goes to the background, and write your own reconnection logic for dropped connections, since OkHttp will not retry for you. Reach for a WebSocket only when a feature genuinely needs bidirectional, real-time updates, like the cooking-together example above. For a plain recipe list or a one-time submission, the request/response tools from earlier lessons remain the simpler, correct choice.
Next up: HTTPS & Network Security Config – making sure every connection your app opens, WebSocket or plain HTTP, is actually secure.