Android Socket.IO保持连接的最佳实践

18

我的Android应用程序使用AndroidAsync库连接到一个带有Socket.IO客户端的Node.js服务器。该套接字客户端是通过服务建立的。

  • 当应用程序开启时,套接字需要保持打开/连接状态
  • 当应用程序关闭时,套接字可以关闭
  • 有一种情况例外,当应用程序关闭时套接字需要保持打开状态

我目前在应用程序中的每个ActivityonResume中启动Socket.IO服务,并在onPause中停止。

这似乎非常低效,因为每次按下主页按钮或切换到应用程序中的另一个活动时,我都会停止套接字并创建一个新的。

如何最好地处理上述关于保持套接字打开的要求?

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);
    }

}

你是如何处理你的需求的? - Leonel Machava
尝试使用ActivityLifecycleCallbacks。https://dev59.com/KHA65IYBdhLWcg3wuhIR#13809991 - Amozoss
@LeonelMachava 我最终使用了GCM来处理应用程序在后台时的任何传入请求。虽然我曾经考虑过这个问题,但如果需要,我基本上可以使用另一个服务来保持连接活动状态,例如使用计数器再多保持5-10分钟。 - The Nomad
2个回答

1

我研究了一些未回答的旧问题,发现这个问题。

我认为最好的方法是使用一个自动启动的绑定服务。

使用bindService()可以在onResume()或onStart()中绑定您的活动,并在没有客户端绑定时让您的服务关闭连接并停止自身。

您可以在此处找到更多信息 https://developer.android.com/guide/components/bound-services


1
如果我不使用Service,我是否应该像你提供的那样编写Service?或者我可以简单地重写onStartonStop事件来控制套接字?在这种情况下,当一个活动不可见时,我们将错过套接字事件。然后在onStart中,我们可以重新加载数据(例如聊天消息)并重新启动套接字。这样电池就不会很快耗尽。 - CoolMind
使用绑定服务的@CoolMind,所有活动都绑定到它,您可以在许多活动中使用套接字,而不必在从一个活动导航到另一个活动时重新启动它(实际上在导航时保持套接字连接处于活动状态)。如果您只有一个活动,并且在onStop中处理套接字断开连接,然后在onStart中重新连接和检索数据,则相同。底线是,如果您只有一个活动,则没有“必要”使用服务(尽管将业务逻辑与活动分离始终是一个好习惯)。 - Klitos G.

0

我有一个部分解决方案(我觉得不会解决你的第三个要求)。 我开始写一个简单的多人游戏,只是为了学习游戏编程。 我决定使用TCP连接(为什么?这是另一个问题......),并且在用户进行每个活动(登录 -> 大厅 -> 选择对手...)时,我必须保持连接开启, 我使用了一个单例模式(双重锁校验),其中包含连接套接字。

伪代码

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;        
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接