Socket.io-client-java: Connecting to Socket.IO on a Service

Created on 30 Aug 2015  路  7Comments  路  Source: socketio/socket.io-client-java

Hi guys,
does someone has been able to instantiate a Service to maintain the connection to the Socket server alive in the background? Or have you used a different solution?
I so far tried without any success.
Here's the code:

public class SocketRTMService extends IntentService {

    public SocketRTMService() {
        super("test-service");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("SocketService", "onCreate");
        mContext = getApplicationContext();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("SocketService", "onStartCommand");
        try {
            mSocket = IO.socket(Amity.resolveSocketEndpoint());
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        initSocket();

        mBus.getInstance().register(this);
        return START_REDELIVER_INTENT;
    }

    private void initSocket() {
        mSocket.connect();
        mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
        mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
        //I send the an auth event to the socket.io server

        mSocket.emit("auth", new Auth(mSender));
        mSocket.on("receive", onReceived);

        Log.d("SocketService", "connected " + mSocket.connected());
    }

    private Emitter.Listener onConnectError = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            Log.d("SocketService", "error");
        }
    };

    private Emitter.Listener onReceived = new Emitter.Listener() {
        @Override
        public void call(final Object... args) {
            Log.d("SocketService", "rtm");
...I handle the received message...
  };

    @Subscribe
    public void sendTyping(SendTypingEvent event) {
        mSocket.emit("send", event.getMessage());
    }

    @Subscribe
    public void stopTyping(StopTypingEvent event) {
        mSocket.emit(event.getMessage());
    }


    @Subscribe
    public void sendMessage(SendMessageEvent event) {
        mSocket.emit("send", event.getMessage());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("SocketService", "onDestroy");
        mSocket.disconnect();
        mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectError);
        mSocket.off(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
        mSocket.off("receive", onReceived);

        mBus.getInstance().unregister(this);
    }

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

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("SocketService", "onHandleIntent");
    }
}

What happens is that it goes in onCreate and then onStartCommand and connected returns false.

Most helpful comment

I don't think that you get an instance of the service in the activity, and I don't think also that it will be advice-able as the 2 should stay separated and you don't want to mess with background and background threading.
My advice will be to use an Event Bus (my favourite is Otto) to post the data received from the socket and receive it in ui thread in the activity wherever you need. There also other ways but this, in my opinion is the cleanest and the least error-prone.

All 7 comments

connected method will return false before "connect" event happens.

so, in your opinion is a false negative?

At least, connected() is always false on your code. Please see https://github.com/socketio/socket.io-client-java/issues/84 for running socket.io-client on background.

The .on listener never seem to be fired! I've tried multiple versions of socket IO. This is a fantastic library Ive used on javascript. dont know why Java seem to have a problem :(
Ive tried extending the Service class. Weird thing is my emits work properly but my .on lsiteners dont seem to fire at all! Here is the code :

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

public class SocketService extends Service {
    final String TAG="SocketService";
    JSONObject user_data;
    private Socket socket;

    private final IBinder mBinder = new MyBinder();
    public class MyBinder extends Binder {
        SocketService getService() {
            return SocketService.this;
        }
    }

    @Override
    public void onCreate(){
        super.onCreate();
        try {
            socket = IO.socket("http://serverIP:port/"); //this gets connected
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "onCreate");
        socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {

            @Override
            public void call(Object... args) {
                Log.d(getClass().getCanonicalName(), "Connected to server");
            }

        }).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {

            @Override
            public void call(Object... arg0) {
                Log.d(getClass().getCanonicalName(), "Disconnected from server");
            }

        });
        socket.on("friendcall", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                JSONObject data = (JSONObject) args[0];
                Log.d(TAG, "Handling friendcall");
                try {
                    String callFrom = data.getString("from");
                    Log.d(TAG, "Call from : " + callFrom);
                } catch (JSONException e) {
                    Log.d(TAG, "friend call object cannot be parsed");
                }

            }
        });

        Log.d(TAG, "onStartCommand. Socket should be up");
        socket.connect();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Bundle extras = intent.getExtras();
        if(extras!=null){
            try {
                user_data = new JSONObject(extras.getString("USER_DATA"));
                socket.emit("giveMeID", user_data.getString("email"));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        return Service.START_NOT_STICKY;
    }


    @Override
    public IBinder onBind(Intent intent) {
        //TODO for communication return IBinder implementation
        return null;
    }

    @Override
    public void onDestroy(){
        socket.disconnect();
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }

}

Retried with the new version 0.7.0 and updated the auth that I was sending and now it works!

public class SocketRTMService extends IntentService {

    private static final String TAG = "SocketService";
    private Socket mSocket;
    private Context mContext;
    private String mSender;

    public SocketRTMService() {
        super(TAG);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
        mContext = getApplicationContext();
        mSender = Prefs.getUserGuid(mContext);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        try {
            IO.Options opts = new IO.Options();
            opts.reconnection = true;
            mSocket = IO.socket(Amity.resolveSocketEndpoint(), opts);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        initSocket();

        mBus.getInstance().register(this);
        return START_REDELIVER_INTENT;
    }

    private void initSocket() {
        mSocket.connect();
        mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
        mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
        mSocket.on(Socket.EVENT_CONNECT, onConnect);
        mSocket.on(Socket.EVENT_RECONNECT, onConnect);
        mSocket.on(Socket.EVENT_DISCONNECT, onConnectError);

        //I send the an auth event to the socket.io server

        auth();
        mSocket.on("receive", onReceived);
    }

    public void auth() {
        String mSender = Prefs.getUserGuid(mContext);
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("user_guid", mSender);
            jsonObject.put("device_id", "android");
            this.mSocket.emit("auth", jsonObject);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    private Emitter.Listener onConnect = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            Log.d(TAG, "connected " + mSocket.connected());
        }
    };

    private Emitter.Listener onConnectError = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            Log.d(TAG, "error " + args[0].toString());
        }
    };

    private Emitter.Listener onReceived = new Emitter.Listener() {
        @Override
        public void call(final Object... args) {
            Log.d(TAG, "received rtm");
            // I parse the object...
        }

    };


    @Subscribe
    public void sendMessage(SendMessageEvent event) {
        mSocket.emit("send", event.getMessage());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        mSocket.disconnect();
        mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectError);
        mSocket.off(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
        mSocket.off(Socket.EVENT_CONNECT, onConnect);
        mSocket.off("receive", onReceived);

        mBus.getInstance().unregister(this);
    }

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

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "onHandleIntent");
    }

@emanuelet Nice and clean code. I am having a video chat app and wanna refactor my socket side of code to a service something like above, however I have one bottleneck. I need to apply my Emitters in my activity the reason is I have a lot of inner classes in that activity. do you know if there is a way to get instance of socket service inside my activity? or if that's not an option, how do you add some instances and data from activity to your emitter service?

I don't think that you get an instance of the service in the activity, and I don't think also that it will be advice-able as the 2 should stay separated and you don't want to mess with background and background threading.
My advice will be to use an Event Bus (my favourite is Otto) to post the data received from the socket and receive it in ui thread in the activity wherever you need. There also other ways but this, in my opinion is the cleanest and the least error-prone.

Was this page helpful?
0 / 5 - 0 ratings