18

My Android app is using the AndroidAsync library to connect to a Node.js server with the Socket.IO client. The socket client is established through a service.

  • Socket needs to stay open/connected when app is open
  • Socket can close when app is not open
  • There is one exception where socket needs to stay open while app is not open

I am currently starting the Socket.IO service in the onResume and stopping in onPause of each Activity in the app.

This seems really inefficient because I basically stopping the socket and recreating a new one every time I press the home button, or switch to another activity in the app.

What would be the best way to handle the above requirements about keeping the socket open?

public class SocketIOService extends Service {

    private Preferences prefs;
    private SocketIOClient socket;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        try {
            ConnectCallback callback = new ConnectCallback() {
                @Override
                public void onConnectCompleted(Exception ex, SocketIOClient client) {
                    if (ex != null) {
                        ex.printStackTrace();
                        return;
                    }

                    Log.v("SOCKET IO CONNECTION", "ESTABLISHED");

                    client.setDisconnectCallback(new DisconnectCallback() {
                        @Override
                        public void onDisconnect(Exception e) {
                            Log.v("SOCKET IO CONNECTION", "TERMINATED");
                        }
                    });

                    client.setErrorCallback(new ErrorCallback() {
                        @Override
                        public void onError(String error) {
                            Log.e("SOCKET IO ERROR", error);
                        }
                    });

                    client.setExceptionCallback(new ExceptionCallback() {
                        @Override
                        public void onException(Exception e) {
                            e.printStackTrace();
                        }
                    });

                    client.setReconnectCallback(new ReconnectCallback() {
                        @Override
                        public void onReconnect() {
                            Log.v("SOCKET IO CONNECTION", "RECONNECTED");
                        }
                    });

                    client.on(EVENT_NEWS, new EventCallback() {
                        @Override
                        public void onEvent(JSONArray argument, Acknowledge acknowledge) {
                            Log.v("EVENT:NEWS", argument.toString());
                        }
                    });

                    client.on(EVENT_MESSAGE_RECEIVE, new EventCallback() {
                        @Override
                        public void onEvent(JSONArray argument, Acknowledge acknowledge) {
                            handleMessageReceive(argument);
                        }
                    });

                }
            };

            socket = SocketIOClient.connect(AsyncHttpClient.getDefaultInstance(), URL_SERVER, callback).get();

            JSONArray array = new JSONArray();
            JSONObject obj = new JSONObject();
            prefs = new Preferences(this);
            try {
                obj.put(KEY_USER_ID, prefs.getUserId());
            } catch (JSONException e) {
                e.printStackTrace();
            }
            array.put(obj);
            socket.emit(EVENT_LOG_USER_ID, array);

            Log.v("SOCKET LOG USER ID", array.toString());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        // If we get killed, after returning from here, restart
        return START_REDELIVER_INTENT;
    }

    @Override
    public void onDestroy() {
        Log.v("SOCKET IO SERVICE", "STOPPED");
        if (socket != null) {
            if (socket.isConnected()) {
                socket.disconnect();
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void handleMessageReceive(JSONArray json) {
        ChatMessage message = JReader.createMessage(json);
    }

}
Eliasz Kubala
  • 3,836
  • 1
  • 23
  • 28
The Nomad
  • 7,155
  • 14
  • 65
  • 100
  • How did you end up handling your requirements? – Leonel Machava May 23 '14 at 08:16
  • Try using ActivityLifecycleCallbacks http://stackoverflow.com/questions/3667022/android-is-application-running-in-background/13809991#13809991 – Amozoss Jul 09 '14 at 16:27
  • @LeonelMachava I ended up using GCM to handle any incoming requests when the app was in the background. Although I had thought about it and could basically use another service to keep the connection alive for say 5-10 minutes more if I wanted to with a counter. – The Nomad Jul 20 '14 at 20:41

2 Answers2

1

Looking into old unanswered questions and found this one. I think the best approach is to use a bound Service that autostarts.

Using the bindService() you can bind your Activities in onResume() or onStart() and let your service close the connection and stop itself when there is no client bound on it.

You can find more information here https://developer.android.com/guide/components/bound-services

Klitos G.
  • 806
  • 5
  • 14
  • 1
    If I don't use `Service`, should I write `Service` as you offered? Or can I simply override `onStart` and `onStop` events to control socket? In this case, when an activity is not visible, we will miss socket events. Then in `onStart` we can reload data (for instance, chat messages) and restart the socket. This way a battery won't drain fast. – CoolMind Jul 29 '20 at 16:36
  • @CoolMind with a bound service, where all activities are binding to it, you gain that you can be using the socket from many activities and not having it restart when you are navigating from one activity to another (practically keeping your socket connection alive while navigating). If you only have one activity and you handle socket disconnection in onStop and then reconnection and data retrieval in onStart, then it's the same. Bottom line,, no 'need' to use a service if you're only on one Activity (although it's always good to separate your business logic from Activities) – Klitos G. Dec 23 '21 at 08:09
0

I have partial solution(won't solve your third requirement I think). I started to wrote a simple multi player game just for studing game programming. I decided to use TCP conntection(why? its another issue...), and I had to keep the connection open while the user proceeded in each Acitivty(login -> lobby -> choose opponent...), I used a Singleton patthern(double-locking check), containing the connection socket.

pseudo code

class ClientConnection{
    private static ClientConnection conn = null;
    Socket mSocket; // your socket  
    protected ClientConnection(){
        mSocket = new Socket();
        // mSocket.connect( bla bla bla ) 

    }

    public Socket getSocket(){
        return mSocket();
    }
    public static ClientConnection getInstance(){
        if(conn == null){
            synchronized (LOCK) {
                if(conn == null){
                    conn = new ClientConnection();
                }
            }
        }
        return conn;        
    }
}
Oss
  • 4,232
  • 2
  • 20
  • 35
YyYo
  • 651
  • 5
  • 13