Как закрыть WebSocket соединение и какие сложности с этим связаны
Что проверяют
Знаете ли вы, что закрытие WebSocket это двустороннее рукопожатие, а не просто обрыв сокета, и умеете ли увязать соединение с жизненным циклом Android. Следом обычно спрашивают, что будет, если пропала сеть или приложение ушло в фон.
Как отвечать
На Android почти всегда работают через OkHttp: client.newWebSocket(request, listener). Закрыть соединение можно двумя способами:
close(code, reason): корректное закрытие. Уже поставленные в очередь сообщения будут отправлены, затем уходит кадр Close. Метод возвращаетfalse, если закрытие уже идёт или сокет отменён. Код обычно1000(нормальное закрытие) или1001(клиент уходит), причина не длиннее 123 байт в UTF-8, иначе будетIllegalArgumentException;cancel(): немедленный обрыв, очередь сообщений выбрасывается. Нужен, когда сервер не отвечает на Close.
Колбэки WebSocketListener стоит знать точно: onClosing приходит, когда закрытие начал сервер, и в ответ принято вызвать свой close(); onClosed означает, что обе стороны закончили; onFailure вызывается при сетевой ошибке, и после него onClosed уже не будет. Колбэки приходят в фоновом потоке OkHttp, обновление UI нужно переносить на главный.
Соединение живёт во ViewModel или репозитории, а не в Activity. Удобно обернуть его в callbackFlow и закрывать в awaitClose { webSocket.close(1000, null) }, тогда сокет закроется вместе с отменой корутины.
Коды 1005 и 1006 зарезервированы: отправить их нельзя, они только описывают закрытие без кода и обрыв без кадра Close.
Где спотыкаются
Не различают намеренное закрытие и обрыв. Переподключение с экспоненциальной задержкой должно срабатывать на onFailure и неожиданные коды, а после вашего close(1000, ...) не должно.
Надеются, что обрыв сети заметят сразу. TCP может молчать минутами, поэтому в OkHttpClient.Builder задают pingInterval, и тогда пропавший ответ на ping приводит к onFailure.
Держат ссылку на Activity в слушателе и забывают закрыть сокет при уходе с экрана. Это утечка памяти и живое соединение, которое продолжает тратить батарею.