Search code examples
androidkotlinwebsocketnetwork-programmingokhttp

How to really disconnect from WebSocket using OkHttpClient?


I'm trying to disconnect from WebSocket connection, but it's listener is still alive, I can see that websockets are still recreating by "OPEN/FAIL" messages using System.out messages.

On the one hand I've tried to release connection in finally block using client.dispatcher().executorService().shutdown() method, but it rejects other future calls (so it doesn't fit), on the other using client.connectionPool().evictAll() doesn't help either (even if I wait, because it may not exit immediately based on docs https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/)

There is WebSocketListener code:

class WebSocketConnectionListener(
    private val updateConnectionValue: (internetConnection: Boolean) -> Unit,
    private val okHttpClient: OkHttpClient,
    private val urlAddress: String
) : WebSocketListener() {

    companion object {
        private const val NORMAL_CLOSURE_STATUS = 1000
    }

    private var isConnected = true
    private var webSocketN: WebSocket? = null

    init {
        createWebSocket()
    }

    override fun onOpen(webSocket: WebSocket, response: Response) {
        println("OPEN: ${response.code}")
        isConnected = true
        updateConnectionValue(true)
        reconnect(webSocket)
    }

    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
        println("FAIL: $t")
        if (!isConnected) {
            updateConnectionValue(false)
        }
        isConnected = false
        reconnect(webSocket)
    }

    private fun createWebSocket() {
        val request = Request.Builder().url(urlAddress).build()
        webSocketN = okHttpClient.newWebSocket(request, this)
    }

    private fun reconnect(webSocket: WebSocket) {
        webSocket.close(NORMAL_CLOSURE_STATUS, null)
        webSocketN?.close(NORMAL_CLOSURE_STATUS, "Connection closed")
        webSocketN = null

        Thread.sleep(3_000)
        createWebSocket()
    }
}

There is DataSource implementation code:

class InternetConnectionDataSourceImpl(
    private val okHttpClient: OkHttpClient,
    private val urlAddress: String
) : InternetConnectionDataSource {

    private fun createWebSocketListener(
        internetConnectionFlow: MutableStateFlow<Boolean>,
    ) = WebSocketConnectionListener(
        updateConnectionValue = { internetConnectionFlow.value = it },
        okHttpClient = okHttpClient,
        urlAddress = urlAddress
    )

    override suspend fun checkConnection(): Flow<Boolean> =
        withContext(Dispatchers.IO) {
            val internetConnectionFlow = MutableStateFlow(true)
            createWebSocketListener(internetConnectionFlow)

            flow {
                try {
                    internetConnectionFlow.collect {
                        emit(it)
                    }
                } finally {
                    println("finally")
                    okHttpClient.connectionPool.evictAll()
                }
            }
        }
}

As the result I'm getting this messages in logcat log messages


Solution

  • Use WebSocket.close() for a graceful shutdown or WebSocket.cancel() for an immediate one.

    https://square.github.io/okhttp/5.x/okhttp/okhttp3/-web-socket/