152

I want to add a Service to my Android app which runs in the background holding a WebSocket connection (possibly over several hours or even days) and regularly sends some data to a server.

Now there seems to be a bunch of WebSocket libraries for Java, and I'm not sure which one I should use:

In addition, there is a native socket.io client library for Android:

  • nkzawa/socket.io-client.java Description from GitHub: Full-featured Socket.IO Client Library for Java, which is compatible with Socket.IO v1.0 and later.

To use the socket.io Android client would be handy for me, because I plan to use nodejs/socket.io for the web frontend anyway. But the native client is quite young and has several open issues. And in addition to that, it is my understanding that an android app does not have any benefit of using the socket.io client library (apart from being compatible with socket.io 1.0 server), because WebSocket support can be assured at the client side.

My requirements are as follows:

  • Compatibility with Android API 9 and higher
  • Possibility to connect via SSL
  • Keep the connection for a long time without having to hold a permanent wakelock
  • Compatibility with an available nodejs websocket server implementation or with socket.io

Any suggestions which one is the right library for these requirements?

Community
  • 1
  • 1
x-ray
  • 3,279
  • 5
  • 24
  • 37
  • Perhaps [Atmosphere](https://github.com/Atmosphere/atmosphere). See [this Question](http://stackoverflow.com/q/9313199/642706). – Basil Bourque May 30 '15 at 16:02
  • Well, maybe their android client Atmosphere/wasync. I updated my question. Any reason to choose this one? :) – x-ray May 30 '15 at 20:16
  • 2
    I'm no expert on WebSocket nor Atmosphere. I only know that Atmosphere is well-worn, used in many projects for [Push](https://en.wikipedia.org/wiki/Push_technology) features including WebSocket support. My only experience is indirect, in building [Vaadin](http://www.Vaadin.com/) web apps. Vaadin uses Atmosphere for its automatic Push capability. But beware, WebSocket is still relatively new with many changes to its definition, specs, and various implementations during its brief history. So expect "issues" no matter how you go. – Basil Bourque May 30 '15 at 20:31
  • websocket from firebase https://github.com/firebase/TubeSock – ant Jan 03 '16 at 18:25
  • 2
    FYI, Autobahn is out there and they have a flashy website. But no notice that "secure WebSockets not implemented" until you spend the time to install and try to run it. Next. – cloudsurfin Feb 13 '16 at 21:10
  • I tried both TooTallNate/Java-WebSocket and koush/AndroidAsync. As you mentioned the former doesn;t handle SSL handskaes well so if you are using a secure connection probably better to use koush/AndroidAsync. AndroidAsyn worked perfectly out of the box. I did like better Java-WebSockets callbacks as they were cleaner. – velval Jun 24 '16 at 01:19
  • OkHttp now supports websockets too: https://medium.com/square-corner-blog/web-sockets-now-shipping-in-okhttp-3-5-463a9eec82d1 – scorpiodawg Apr 16 '17 at 21:23
  • 2
    I don't have enough reputation to comment so I am writing it as answer as I have gone through the same requirements you mentioned in your question and okhttp helped me to satisfy all the requirements. It supports web sockets since the introduction of version 3.5, So its an added advantage to use okHttp(web service calls + web sockets support). Here is the link to start with. – Kaleem Patel Jul 30 '17 at 06:37
  • 12
    Questions like this should not be closed. – Martin Berger Dec 27 '18 at 13:21

3 Answers3

137

Some notes.

  • koush/AndroidAsync does not perform the closing handshake which is required by RFC 6455. See this for details.

  • Project Tyrus works on Android, but make sure that its license (CDDL 1.1 and GPL 2 with CPE) and its size (Reducing WebSocket client jar size with ProGuard) meet your requirements. Also note that Tyrus may throw an exception when a text size is large (it's probably a bug). See this for details.

  • Jetty: A 2-year-ago email thread in jetty-users mailing list says "We currently have no Android compatible Jetty 9 WebSocket client. There are plans to attempt to backport the Jetty WebSocket Client from JDK 7 to JDK 5/6 for android use, but its a lower priority than finishing our implementation of JSR-356 Java WebSocket API (javax.websocket)." Jetty's current document about its WebSocket Client API does not mention anything about Android.

  • codebutler/android-websocket does not perform the closing handshake which is required by RFC 6455 and may throw an exception on close. See this.

  • Atmosphere/wasync uses AsyncHttpClient/async-http-client as its WebSocket implementation. So, rather, AsyncHttpClient/async-http-client should be mentioned instead.

  • firebase/TubeSock does not verify Sec-WebSocket-Accept. This is a violation against RFC 6455. Also, TubeSock has a bug in building a text message. You will encounter the bug sooner or later if you use multi-byte UTF-8 characters for text messages. See Issue 3 in delight-im/Android-DDP for a long list about TubeSock problems.

Consideration Points

Consideration points in selecting a WebSocket client implementation written in Java:

  1. Compliance. Not a small number of implementations do not implement the closing handshake required by RFC 6455. (What happens if the closing handshake is not implemented? See this.)
  2. Required Java version. Java SE 5, 6, 7, 8 or Java EE? Works even on Android?
  3. Size. Some implementations have many dependencies.
  4. wss support.
  5. HTTP proxy support.
  6. wss over HTTP proxy support. See Figure 2 in How HTML5 Web Sockets Interact With Proxy Servers about what a WebSocket client library has to do to support wss over HTTP proxy.
  7. Flexibility on SSL configuration. SSLSocketFactory and SSLContext should be able to be utilized without unnecessary restrictions.
  8. Custom HTTP headers in the opening handshake, including Basic Authentication.
  9. Custom HTTP headers in HTTP proxy negotiation, including authentication at the proxy server.
  10. Capable of sending all the frame types (continuation, binary, text, close, ping and pong) or not. Most implementations do not provide developers with means to send fragmented frames and unsolicited pong frames manually.
  11. Listener interface to receive various WebSocket events. A poor interface makes developers frustrated. A rich interface helps developers write robust applications.
  12. Able to inquire WebSocket state or not. RFC 6455 defines CONNECTING, OPEN, CLOSING and CLOSED states, but few implementations maintain their internal state transition in the defined way.
  13. Able to set a timeout value for socket connection. (Equivalent to the second argument of Socket.connect(SocketAddress endpoint, int timeout) method)
  14. Able to access the underlying raw socket.
  15. Intuitive easy-to-use API or not.
  16. Well-documented or not.
  17. RFC 7692 (Compression Extensions for WebSocket) support (aka permessage-deflate).
  18. Redirection (3xx) support.
  19. Digest Authentication support.

nv-websocket-client covers all the above except the last two. In addition, one of its small but convenient features is to send ping/pong frames periodically. It can be achieved just by calling setPingInterval/setPongInterval methods (See JavaDoc).

Disclaimer: Takahiko Kawasaki is the author of nv-websocket-client.

Community
  • 1
  • 1
Takahiko Kawasaki
  • 18,118
  • 9
  • 62
  • 105
  • 1
    is nv-websocket-client library still under development ? I faced automatic disconnection issue with TooTallNate/Java-WebSockets with error 1006 and NO reason.. does this nv-websocket also resolve it ? – Ankit Bansal Oct 09 '15 at 11:58
  • nv-websocket-client is stable. To prevent automatic disconnection, you should send ping (or pong) frames periodically. To do it by TooTallNate/Java-WebSocket, you have to implement a periodical task (using Timer, Thread, ExecutorService, etc.) that constructs a `Framedata` instance representing a ping (or pong) frame and sends it by `WebSocket.sendFrame(Framedata)`. If you use nv-websocket-client, it can be achived by just calling [WebSocket.setPingInterval(int)](http://takahikokawasaki.github.io/nv-websocket-client/com/neovisionaries/ws/client/WebSocket.html#setPingInterval-long-). – Takahiko Kawasaki Oct 09 '15 at 14:35
  • 1
    As for 1006, the specification (RFC 6455) states that the code _MUST NOT be set as a status code in a Close control frame by an endpoint_. This means that the code was generated on the client side. You can get more information about disconnection through `onDisconnected` method and `onError` method of [WebSocketListener](http://takahikokawasaki.github.io/nv-websocket-client/com/neovisionaries/ws/client/WebSocketListener.html). `onError` method gives you a `WebSocketException` instance. Call its `getError()` method to see what is the problem. – Takahiko Kawasaki Oct 09 '15 at 14:36
  • Thanks for the reply, I am sending periodic (20 sec) text messages to server.. and receiving response from server as well (after every 120 sec) As far as this error is concerned, the callback is coming in onClose method without any defined error...I hope sending text messages are no different than ping/pong ? Anyways, I will try your library as well. Thanks – Ankit Bansal Oct 10 '15 at 02:39
  • 7
    For wss, I tried okhttp and autobahn (also suspect of the self promotion in this answer). Autobahn was easy, but has no SSL. OkHttp has little to zero (consolidated) documentation (Feb 2016). I wasted a lot of time reading their code and their exceptions because I wasn't privy to workarounds (like set the timeout to 0, or close the incoming message) to get a bare bones example working. Dumping those two (and my frustration), I found nv (refreshingly) well documented; it worked without a fuss. – cloudsurfin Feb 13 '16 at 22:55
  • cannot thank you enough. This should be the go-to java websocket implementation. No android ssl trickery and permessage-deflate is critical. – Vans S Jun 23 '16 at 18:24
  • it's said on the `async-http-client` readme that it *currently requires JDK8*. That pretty much excludes Android, right? – Display Name Oct 29 '16 at 15:43
  • 1
    Any thoughts on Square/okhttp's new websockets support? https://medium.com/square-corner-blog/web-sockets-now-shipping-in-okhttp-3-5-463a9eec82d1 – scorpiodawg Apr 16 '17 at 21:24
  • @TakahikoKawasaki I'd like to reiterate scorpiodawg's question -- both NV and OkHttp have changed *a lot* since this answer was posted, and a lot of the comparison is now invalid. Care to make an update? You don't have to, like, rewrite the answer, but if you'd just comment here and mention how you see things now, it would be great. – forresthopkinsa Jul 19 '17 at 19:45
  • 2
    I don't know details about OkHttp. I'm sorry I'm so busy as the founder of [Authlete, Inc.](https://www.authlete.com/) ("[API security startup Authlete raises $1.2m in seed funding](https://www.techinasia.com/authlete-gets-seed-funding-500-startups)") I can't spare time to look into OkHttp and update the list of consideration points. Regarding changes since my answer, see [CHANGES.md](https://github.com/TakahikoKawasaki/nv-websocket-client/blob/master/CHANGES.md). Please note nv-websocket-client is just my hobby while OkHttp seems a big project having 138 contributors. – Takahiko Kawasaki Jul 19 '17 at 20:25
  • @TakahikoKawasaki Totally understandable. Thanks! And good luck with your startup! – forresthopkinsa Jul 19 '17 at 20:49
  • You are a true hero! – Sevastyan Savanyuk Aug 16 '17 at 12:34
  • Let me be the lucky hundredth! – azizbekian Feb 01 '18 at 10:41
  • In nv-websocket-client source code (ws/client/WritingThread.java), I saw `synchronized(this)` is used everywhere, I really concern about the performance - I'm trying to use LMAX Disruptor to achieve lock-free in the main path (consists of a set of data processing pipelines) of my own program. I'm thinking a ConcurrentLinkedQueue or BlockedLinkedQueue interface from the client may fit my writing the best. – zipper Oct 21 '20 at 01:40
4

Some other considerations:

Tyrus works on Android. However, the SSL libraries it uses in Android 5.0 are buggy and fail SSL handshakes. This is supposed to be fixed in newer versions of Android, but with the way that Android is not updated on many devices, this may be a problem for you.

Depending on how SSL is implemented for other websocket implementations, this may also be a problem.

AndroidAsync does not have this SSL issue. It does have other issues like not being able to set timeouts.

Community
  • 1
  • 1
mattm
  • 5,851
  • 11
  • 47
  • 77
1

a) Add this file in gradle file

compile 'com.github.nkzawa:socket.io-client:0.3.0'

b) Add these lines in Application Activity:

    public class MyApplication extends Application {
     private Socket mSocket;
        {
            try {
               mSocket = IO.socket(Config.getBaseURL());

            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }

        public Socket getSocket() {
            return mSocket;
        }
}

c) Add this function to your activity, where you called WebSocket:

     private void websocketConnection() {
            //Get websocket from application
            MyApplication app = (MyApplication ) getApplication();
            mSocket = app.getSocket();
            mSocket.on(Socket.EVENT_CONNECT, onConnect);
            mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
            mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
            mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
            mSocket.on("messageFromServer", onNewLocation);
            mSocket.connect();
        } 


    private Emitter.Listener onConnect = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            runOnUiThread(() -> {
                if (!isConnected) {

                    RequestSocket mRequestSocket = new RequestSocket();

                    mRequestSocket.setToken("anil_singhania");
                   /* your parameter */
                    mSocket.emit("messageFromClient", new Gson().toJson(mRequestSocket));
                    Log.i("Socket Data", new Gson().toJson(mRequestSocket));
                    isConnected = true;
                }
            });
        }
    };

    private Emitter.Listener onDisconnect = args -> runOnUiThread(() -> {
        isConnected = false;
       /* Toast.makeText(getApplicationContext(),
                R.string.disconnect, Toast.LENGTH_LONG).show();*/
    });

    private Emitter.Listener onConnectError = args -> runOnUiThread(() -> {
         /*   Toast.makeText(getApplicationContext(),
            R.string.error_connect, Toast.LENGTH_LONG).show()*/
    });

    private Emitter.Listener onNewLocation = new Emitter.Listener() {
        @Override
        public void call(final Object... args) {
            runOnUiThread(() -> {


            });
        }
    };
TheTechGuy
  • 1,568
  • 4
  • 18
  • 45
Anil Singhania
  • 785
  • 10
  • 9