{"id":20509265,"url":"https://github.com/futuremind/rxwebsocket","last_synced_at":"2025-04-13T22:22:29.934Z","repository":{"id":72491961,"uuid":"266715707","full_name":"FutureMind/rxwebsocket","owner":"FutureMind","description":"Reactive wrapper for okhttp websocket","archived":false,"fork":false,"pushed_at":"2020-07-11T16:46:45.000Z","size":170,"stargazers_count":31,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-27T12:46:28.239Z","etag":null,"topics":["android","okhttp","reactive","rxjava","websocket"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FutureMind.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-25T07:42:30.000Z","updated_at":"2023-11-01T01:55:41.000Z","dependencies_parsed_at":"2023-05-13T05:45:28.608Z","dependency_job_id":null,"html_url":"https://github.com/FutureMind/rxwebsocket","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FutureMind%2Frxwebsocket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FutureMind%2Frxwebsocket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FutureMind%2Frxwebsocket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FutureMind%2Frxwebsocket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FutureMind","download_url":"https://codeload.github.com/FutureMind/rxwebsocket/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248789774,"owners_count":21161891,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","okhttp","reactive","rxjava","websocket"],"created_at":"2024-11-15T20:23:39.322Z","updated_at":"2025-04-13T22:22:29.902Z","avatar_url":"https://github.com/FutureMind.png","language":"Kotlin","readme":"# RxWebSocket\n\n[![](https://jitpack.io/v/FutureMind/rxwebsocket.svg)](https://jitpack.io/#FutureMind/rxwebsocket) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nRxWebSocket is a simple, lightweight, reactive wrapper around [OkHttp WebSocket](https://square.github.io/okhttp/4.x/okhttp/okhttp3/-web-socket/), inspired by [RxAndroidBle](https://github.com/Polidea/RxAndroidBle).\n\nInstead of implementing a `WebSocketListener` like you would normally do with `WebSocket`, you can subscribe to it and when it is connected, subscribe to its messages. When you're done with the connection, you can simply unsubscribe and it takes care of closing the connection for you.\n\n## Usage\n\nTL;DR, take me to a [real life example](#real-life-example).\n\n### Connecting\n\nSimply prepare you `OkhttpClient` and a regular `okhttp3.Request`.\n\n```kotlin\nval okHttpClient = OkHttpClient.Builder().build()\nval request = Request.Builder().url(...).build()\n\nRxWebSocket(okHttpClient, request)\n    .connect()\n    .subscribe()\n\n```\n\n### Socket states\n\nThe `RxWebSocket.connect()` returns a `Flowable` with different socket states to subscribe to.\n\n| Rx event | State | Description |\n|-------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| onNext | Connecting | Returned immediately after subscribing. \u003cbr\u003eYou can start sending at this stage and the messages will be queued for when the socket connects. \u003cbr\u003eCorresponds to the state before `onOpen` in `WebSocketListener`. |\n| onNext | Connected | Returned when the socket has successfully opened connection. \u003cbr\u003eYou can send messages while in this state. \u003cbr\u003eYou can subscribe to `messageFlowable` or `byteMessageFlowable` in this state.\u003cbr\u003eCorresponds to `onOpen` in `WebSocketListener`. |\n| onNext | Disconnecting | Corresponds to `onClosing` in `WebSocketListener`. |\n| onNext | Disconnected | Corresponds to `onClosed` in `WebSocketListener`. Right after this event, `onCompleted` is published. |\n| onCompleted | - | The Flowable is completed right after `Disconnected` (`onClosed`). |\n| onError | - | The Flowable signals `SocketConnectionException` which contains the original exception that caused the issue and appropriate `okhttp3.Response`.\u003cbr\u003eCorresponds to `onFailure` in `WebSocketListener`. |\n\n\n---\n\nSo the whole flow can look something like this.\n\n```kotlin\nRxWebSocket(okHttpClient, request)\n    .connect()\n    .switchMap { state -\u003e\n        when(state) {\n            is SocketState.Connecting -\u003e TODO()\n            is SocketState.Connected -\u003e TODO()\n            SocketState.Disconnecting -\u003e TODO()\n            SocketState.Disconnected -\u003e TODO()\n        }\n    }\n    .doOnError { TODO(\"Handle socket connection failed\") }\n    .doOnComplete { TODO(\"Handle socket connection closed gracefully\") }\n    .subscribe()\n```\n\nIt's good to use `switchMap` here to make sure that when the state changes, you unsubscribe from e.g. `Connected.messageFlowable`.\n\n### Sending messages\n\n`Connecting` and `Connected` implement `SendCapable`. In both these states you can send messages, although in `Connecting` the messages will be queued and sent when it's possible (`okhttp` exposes such mechanism).\n\n```kotlin\nRxWebSocket(okHttpClient, request)\n    .connect()\n    .ofType(SocketState.SendCapable::class.java)\n    .flatMapCompletable { state -\u003e\n        Completable.fromCallable { state.send(\"Hello world\") }\n    }\n    .subscribe()\n```\n\n### Receiving messages\n\n```kotlin\nRxWebSocket(okHttpClient, request)\n    .connect()\n    .switchMap { state -\u003e\n        when (state) {\n            is SocketState.Connected -\u003e state.messageFlowable()\n            else -\u003e Flowable.never()\n        }\n    }\n    .subscribe { msg -\u003e handleMessage(msg) }\n```\n\n### Dealing with disconnection\n\nBecause socket failures cause flowable error and graceful disconnections cause it to complete, you can leverage the power of RxJava's `retry` and `repeat` functions to implement your reconnection logic in a very elegant way.\n\n```kotlin\nRxWebSocket(okHttpClient, request)\n    .connect()\n    .retryWhen { it.delay(3, TimeUnit.SECONDS) }\n    .subscribe()\n```\n\n\n## Real life example\n\nIn real life, you will probably have some `ViewModel` which observes incoming messages to pass them to UI and also accepts new messages e.g. incoming from some input field. Here's a sample implementation of such `ViewModel` (you can aso find it in `sample` directory).\n\n```kotlin\nclass MainViewModel : ViewModel() {\n\n    private val okHttpClient = OkHttpClient.Builder().build()\n    private val request = Request.Builder().url(\"wss://echo.websocket.org\").build()\n\n    private val rxSocket = RxWebSocket(okHttpClient, request)\n\n    private val socketConnection: Flowable\u003cSocketState\u003e = rxSocket\n        .connect()\n        .retryWhen { it.delay(3, TimeUnit.SECONDS) }\n        .replay(1)\n        .autoConnect()\n\n    private val outgoingMessagesProcessor = PublishProcessor.create\u003cString\u003e()\n\n    private val outgoingMessagesDisposable = socketConnection\n        .ofType(SocketState.SendCapable::class.java)\n        .switchMap { state -\u003e\n            outgoingMessagesProcessor.doOnNext { state.send(it) }\n        }\n        .subscribe()\n\n    fun observeSocketState(): Flowable\u003cSocketState\u003e = socketConnection\n\n    fun observeMessages(): Flowable\u003cString\u003e = socketConnection\n        .ofType(SocketState.Connected::class.java)\n        .switchMap { it.messageFlowable() }\n\n    fun sendMessage(message: String) = outgoingMessagesProcessor.onNext(message)\n\n    override fun onCleared() {\n        rxSocket.disconnect(1000, \"\")\n        outgoingMessagesDisposable.dispose()\n    }\n\n}\n```\n\n\nLet's break it down piece by piece.\n\n\nFirst, we prepare our RxWebSocket for connection.\n\n```kotlin\nprivate val okHttpClient = OkHttpClient.Builder().build()\nprivate val request = Request.Builder().url(\"wss://echo.websocket.org\").build()\n\nprivate val rxSocket = RxWebSocket(okHttpClient, request)\n```\n\n---\n\nThen we introduce our connection flowable.\n\n```kotlin\nprivate val socketConnection: Flowable\u003cSocketState\u003e = rxSocket\n        .connect()\n        .retryWhen { it.delay(3, TimeUnit.SECONDS) }\n        .replay(1)\n        .autoConnect()\n```\n\n`.replay(1).autoConnect()` is used to multicast our flowable so that our socket connetion can be easily used by different subscribers. `replay(1)` makes sure that whenever new subscriber arrives, he immediately receives the current state the socket is in.\n\nNotice the `retryWhen` - it is used to make sure, that whenever our connection breaks it is reconnected. Of course this is a very simple example, you can use a much more sophisticated reconnection logic in your `retry` operator.\n\n---\n\n```kotlin\nprivate val outgoingMessagesProcessor = PublishProcessor.create\u003cString\u003e()\n\nprivate val outgoingMessagesDisposable = socketConnection\n    .ofType(SocketState.SendCapable::class.java)\n    .switchMap { state -\u003e\n        outgoingMessagesProcessor.doOnNext { state.send(it) }\n    }\n    .subscribe()\n```\n\nThis code is responsible for processing incoming messages and sending them through the websocket.\n\n---\n\nThis is the interface of our `ViewModel` which should be pretty self-explanatory.\n\n```kotlin\nfun observeSocketState(): Flowable\u003cSocketState\u003e = socketConnection\n\nfun observeMessages(): Flowable\u003cString\u003e = socketConnection\n    .ofType(SocketState.Connected::class.java)\n    .switchMap { it.messageFlowable() }\n\nfun sendMessage(message: String) = outgoingMessagesProcessor.onNext(message)\n```\n\n---\n\nLast but not least, remeber to disconnect from websocket when you're done with it. Calling `disconnect` gracefully closes the socket and completes our `socketConnection`.\n\n```kotlin\n    override fun onCleared() {\n        rxSocket.disconnect(1000, \"\")\n        outgoingMessagesDisposable.dispose()\n    }\n```\n\n## Installation\n\nRxWebSocket is available on jitpack.\n\n```gradle\nrepositories {\n    ...\n    maven { url 'https://jitpack.io' }\n}\n```\n\n```gradle\nimplementation 'com.github.FutureMind:rxwebsocket:1.0'\n```\n\n## License\n\n    The MIT License\n\n    Copyright (c) 2020 Future Mind\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in\n    all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n    THE SOFTWARE.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuturemind%2Frxwebsocket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuturemind%2Frxwebsocket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuturemind%2Frxwebsocket/lists"}