Skip to content

Real-time WebSocket chat

A chat screen asks three things of its connection: be open while the screen is in use, hand each message to the interface, and come back by itself when the network drops. websocket does all three, on the phone and in the Previewer, with nothing to install.

A room in one file

from apkpy_lib import Screen, button, inputs, label, lifecycle, run, websocket

room = Screen(id="room")
status = label("Offline", id="status", screen=room)
latest = label("No messages yet", id="latest", screen=room)
message = inputs("Write a message", id="message", screen=room)


def connected():
    status.set_value("Live")


def received(text):
    latest.set_value(text)


def closed(code, reason):
    status.set_value("Closed: " + reason)


def failed(error):
    status.set_value("Reconnecting")


def connect_room():
    websocket.connect(
        "support-room",
        "wss://api.example.com/chat",
        headers={"Authorization": "Bearer YOUR_TOKEN"},
        on_open=connected,
        on_message=received,
        on_close=closed,
        on_error=failed,
        reconnect=True,
    )


def send_message():
    text = message.get_value()
    if text != "":
        websocket.send("support-room", text)
        message.set_value("")


def leave_room():
    websocket.close("support-room")


button("Send", command=send_message, screen=room)
lifecycle(room, on_resume=connect_room, on_destroy=leave_room)
run(room)

What each part does

  • The connection has a name. "support-room" is how send() and close() find it. Connecting again with the same name replaces the earlier connection, so connecting in on_resume is safe: coming back to the screen does not open a second socket.
  • on_open() runs once the handshake is done.
  • on_message(text) receives each text message. It runs on the interface thread, so it can change components straight away.
  • on_close(code, reason) receives the close code and the reason, both as text.
  • on_error(error) receives a description of what failed. With reconnect=True, a new attempt follows.
  • headers= travel with the upgrade request, which is where a token goes. protocols= lists the subprotocols the server may choose from.

On Android the connection is made with OkHttp, which the build adds only to apps that call websocket.

Sending before the connection is open

send() can be called right after connect(). While the handshake runs, up to 64 messages wait in order and leave as soon as the connection opens. A 65th message pushes out the oldest one, so a long outage cannot fill the phone's memory with unsent text.

When the network drops

With reconnect=True, the default, a connection that fails or is cut tries again by itself:

Argument Default What it does
reconnect_delay 1.0 seconds before the first new attempt
max_reconnect_delay 15.0 the longest wait between attempts
ping_interval 25.0 seconds between pings that keep a quiet connection alive

Each failed attempt doubles the wait, up to max_reconnect_delay, with a small random margin so that many phones do not reconnect in the same second. A connection that opens starts the count again. Only close() stops the attempts.

Mobile networks and home routers often drop connections that stay silent for a minute or two. The ping is what keeps a room that nobody is typing in from being closed under you.

Closing

websocket.close("support-room") closes with code 1000 and stops reconnection. websocket.close("support-room", code=4000, reason="left") sends a code and a reason of your own. Close in on_destroy, as above, or when the person leaves the room.

Trying it before a build

The Previewer opens a real connection from your computer with the same calls, so a server on your machine is enough to try the screen, the reconnection and the messages sent during the handshake before building an APK.

Production responsibilities

  • authenticate the WebSocket upgrade;
  • validate messages on the server;
  • persist delivery state and message history;
  • define ordering, deduplication and retry semantics;
  • use push notifications when the process is not connected.

ApkPy provides WSS framing, ping/pong handling, bounded pending sends and backoff reconnection. It does not provide end-to-end encryption or a chat backend.

For rows that read as a conversation, with bubbles on each side, see Components and layouts; for messages that arrive while the app is closed, see Push with Firebase.