android 基于okhttp的socket封装

代码如下

复制代码
package com.nyw.mvvmmode.net.socket;


import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

/**
 * @author nyw
 * @description 基于 OkHttp WebSocket 的 Socket 连接管理类
 */
public class SocketManager {
    private static final String TAG = "SocketManager";

    // 单例模式
    private static volatile SocketManager instance;

    private OkHttpClient okHttpClient;
    private WebSocket webSocket;

    // 连接状态
    private boolean isConnected = false;

    // 连接地址
    private String socketUrl;

    // 重连间隔时间(毫秒)
    private static final long RECONNECT_INTERVAL = 5000;

    private SocketListener socketListener;

    private SocketManager() {
        // 初始化 OkHttpClient
        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
                .build();
    }

    public static SocketManager getInstance() {
        if (instance == null) {
            synchronized (SocketManager.class) {
                if (instance == null) {
                    instance = new SocketManager();
                }
            }
        }
        return instance;
    }

    /**
     * 设置 Socket 连接地址
     */
    public void setSocketUrl(String url) {
        this.socketUrl = url;
    }

    /**
     * 连接 Socket
     */
    public void connect() {
        if (isConnected || socketUrl == null) {
            return;
        }

        Request request = new Request.Builder().url(socketUrl).build();
        webSocket = okHttpClient.newWebSocket(request, webSocketListener);
        isConnected = true;
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        if (webSocket != null) {
            webSocket.close(1000, "主动断开连接");
            webSocket = null;
        }
        isConnected = false;
    }

    /**
     * 发送文本消息
     */
    public void sendMessage(String message) {
        if (webSocket != null && isConnected) {
            webSocket.send(message);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    /**
     * 发送二进制消息
     */
    public void sendMessage(ByteString byteString) {
        if (webSocket != null && isConnected) {
            webSocket.send(byteString);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    /**
     * 判断是否连接
     */
    public boolean isConnected() {
        return isConnected;
    }

    /**
     * 设置 Socket 回调监听
     */
    public void setSocketListener(SocketListener listener) {
        this.socketListener = listener;
    }

    /**
     * WebSocket 回调监听
     */
    private final WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "Socket 连接成功");
            isConnected = true;
            if (socketListener != null) {
                socketListener.onConnected();
            }
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
            super.onMessage(webSocket, text);
            Log.d(TAG, "收到文本消息: " + text);
            if (socketListener != null) {
                socketListener.onMessage(text);
            }
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
            super.onMessage(webSocket, bytes);
            Log.d(TAG, "收到二进制消息: " + bytes.hex());
            if (socketListener != null) {
                socketListener.onMessage(bytes);
            }
        }

        @Override
        public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosing(webSocket, code, reason);
            Log.d(TAG, "Socket 正在关闭: " + reason);
            isConnected = false;
            if (socketListener != null) {
                socketListener.onDisconnected(reason);
            }
        }

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "Socket 已关闭: " + reason);
            isConnected = false;
            if (socketListener != null) {
                socketListener.onDisconnected(reason);
            }
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "Socket 连接失败: " + t.getMessage());
            isConnected = false;
            if (socketListener != null) {
                socketListener.onError(t.getMessage());
            }
            // 自动重连
            reconnect();
        }
    };

    /**
     * 自动重连
     */
    private void reconnect() {
        if (!isConnected) {
            new Thread(() -> {
                try {
                    Thread.sleep(RECONNECT_INTERVAL);
                    connect();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    /**
     * Socket 回调接口
     */
    public interface SocketListener {
        void onConnected();
        void onDisconnected(String reason);
        void onMessage(String text);
        void onMessage(ByteString bytes);
        void onError(String error);
    }
}

使用示例

复制代码
// 初始化并连接
SocketManager.getInstance()
        .setSocketUrl("ws://your.socket.url")
        .setSocketListener(new SocketManager.SocketListener() {
            @Override
            public void onConnected() {
                Log.d("SocketTest", "连接成功");
            }

            @Override
            public void onDisconnected(String reason) {
                Log.d("SocketTest", "断开连接: " + reason);
            }

            @Override
            public void onMessage(String text) {
                Log.d("SocketTest", "收到消息: " + text);
            }

            @Override
            public void onMessage(ByteString bytes) {
                Log.d("SocketTest", "收到二进制消息");
            }

            @Override
            public void onError(String error) {
                Log.e("SocketTest", "错误: " + error);
            }
        })
        .connect();

// 发送消息
SocketManager.getInstance().sendMessage("Hello Socket Server");

// 断开连接
SocketManager.getInstance().disconnect();

功能特点

单例模式 ,全局唯一 Socket 连接✅ 自动重连 (连接失败或断开时自动尝试重连)✅ 支持文本和二进制消息统一回调接口 (连接、断开、接收消息、错误)✅ 线程安全(OkHttp 内部处理)


5. 注意事项

  • WebSocket URL 协议用 ws://(非加密)或 wss://(加密)
  • 如果需要在 UI 线程更新界面,回调中要切换到主线程(可以用 Handler 或 LiveData)
  • 长连接要考虑服务器心跳检测机制

另一个封装,把这个 Socket 管理类和 MVVM 结合,用 LiveData 把消息分发到 ViewModel同时也可以支持单独不在mvvm使用

设计思路

  • SocketManager 保留原来的 SocketListener 回调方式(支持独立使用)
  • 新增一个 LiveData 适配层,让 Socket 收到的消息自动 post 到 LiveData
  • ViewModel 中只需要持有 LiveData,UI 层观察它就能自动更新

改造后的 SocketManager(支持 LiveData)

复制代码
package com.nyw.mvvmmode.net.socket;

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

/**
 * 基于 OkHttp WebSocket 的 Socket 连接管理类
 * 支持 MVVM + LiveData,也可独立使用
 */
public class SocketManager {
    private static final String TAG = "SocketManager";
    private static volatile SocketManager instance;

    private OkHttpClient okHttpClient;
    private WebSocket webSocket;
    private boolean isConnected = false;
    private String socketUrl;

    // 重连间隔
    private static final long RECONNECT_INTERVAL = 5000;

    // 独立使用的回调
    private SocketListener socketListener;

    // 用于 MVVM 的 LiveData
    private final MutableLiveData<String> messageLiveData = new MutableLiveData<>();
    private final MutableLiveData<ByteString> binaryLiveData = new MutableLiveData<>();
    private final MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
    private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();

    private SocketManager() {
        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
                .build();
    }

    public static SocketManager getInstance() {
        if (instance == null) {
            synchronized (SocketManager.class) {
                if (instance == null) {
                    instance = new SocketManager();
                }
            }
        }
        return instance;
    }

    public void setSocketUrl(String url) {
        this.socketUrl = url;
    }

    public void connect() {
        if (isConnected || socketUrl == null) return;

        Request request = new Request.Builder().url(socketUrl).build();
        webSocket = okHttpClient.newWebSocket(request, webSocketListener);
        isConnected = true;
    }

    public void disconnect() {
        if (webSocket != null) {
            webSocket.close(1000, "主动断开连接");
            webSocket = null;
        }
        isConnected = false;
    }

    public void sendMessage(String message) {
        if (webSocket != null && isConnected) {
            webSocket.send(message);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    public void sendMessage(ByteString byteString) {
        if (webSocket != null && isConnected) {
            webSocket.send(byteString);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    public boolean isConnected() {
        return isConnected;
    }

    // ====== 独立使用的回调 ======
    public void setSocketListener(SocketListener listener) {
        this.socketListener = listener;
    }

    // ====== MVVM LiveData 接口 ======
    public LiveData<String> getMessageLiveData() {
        return messageLiveData;
    }

    public LiveData<ByteString> getBinaryLiveData() {
        return binaryLiveData;
    }

    public LiveData<Boolean> getConnectionLiveData() {
        return connectionLiveData;
    }

    public LiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    /**
     * WebSocket 回调
     */
    private final WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "Socket 连接成功");
            isConnected = true;

            // 回调
            if (socketListener != null) socketListener.onConnected();
            connectionLiveData.postValue(true);
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
            super.onMessage(webSocket, text);
            Log.d(TAG, "收到文本消息: " + text);

            // 回调
            if (socketListener != null) socketListener.onMessage(text);
            messageLiveData.postValue(text);
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
            super.onMessage(webSocket, bytes);
            Log.d(TAG, "收到二进制消息: " + bytes.hex());

            // 回调
            if (socketListener != null) socketListener.onMessage(bytes);
            binaryLiveData.postValue(bytes);
        }

        @Override
        public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosing(webSocket, code, reason);
            Log.d(TAG, "Socket 正在关闭: " + reason);
            isConnected = false;

            if (socketListener != null) socketListener.onDisconnected(reason);
            connectionLiveData.postValue(false);
        }

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "Socket 已关闭: " + reason);
            isConnected = false;

            if (socketListener != null) socketListener.onDisconnected(reason);
            connectionLiveData.postValue(false);
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "Socket 连接失败: " + t.getMessage());
            isConnected = false;

            if (socketListener != null) socketListener.onError(t.getMessage());
            errorLiveData.postValue(t.getMessage());

            // 自动重连
            reconnect();
        }
    };

    private void reconnect() {
        if (!isConnected) {
            new Thread(() -> {
                try {
                    Thread.sleep(RECONNECT_INTERVAL);
                    connect();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    /**
     * 独立使用的回调接口
     */
    public interface SocketListener {
        void onConnected();
        void onDisconnected(String reason);
        void onMessage(String text);
        void onMessage(ByteString bytes);
        void onError(String error);
    }
}

在 MVVM 中使用(ViewModel + LiveData)

复制代码
package com.nyw.mvvmmode.viewmodel;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import com.nyw.mvvmmode.net.socket.SocketManager;
import com.nyw.mvvmmode.utils.ToastUtils;

import okio.ByteString;

public class SocketViewModel extends AndroidViewModel {

    private final SocketManager socketManager;

    public SocketViewModel(@NonNull Application application) {
        super(application);
        socketManager = SocketManager.getInstance();
        socketManager.setSocketUrl("ws://your.socket.url");
        socketManager.connect();
    }

    // 暴露 LiveData 给 UI
    public LiveData<String> getMessageLiveData() {
        return socketManager.getMessageLiveData();
    }

    public LiveData<Boolean> getConnectionLiveData() {
        return socketManager.getConnectionLiveData();
    }

    public LiveData<String> getErrorLiveData() {
        return socketManager.getErrorLiveData();
    }

    // 发送消息
    public void sendMessage(String msg) {
        socketManager.sendMessage(msg);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        socketManager.disconnect();
    }
}

在 Activity/Fragment 中观察

复制代码
public class SocketActivity extends AppCompatActivity {

    private SocketViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_socket);

        viewModel = new ViewModelProvider(this).get(SocketViewModel.class);

        // 观察连接状态
        viewModel.getConnectionLiveData().observe(this, isConnected -> {
            if (isConnected) {
                Toast.makeText(this, "Socket 连接成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Socket 已断开", Toast.LENGTH_SHORT).show();
            }
        });

        // 观察文本消息
        viewModel.getMessageLiveData().observe(this, msg -> {
            Log.d("SocketActivity", "收到消息: " + msg);
            // 更新UI
        });

        // 观察错误
        viewModel.getErrorLiveData().observe(this, error -> {
            Toast.makeText(this, "Socket 错误: " + error, Toast.LENGTH_SHORT).show();
        });
    }
}

单独使用(不依赖 MVVM)

复制代码
SocketManager.getInstance()
        .setSocketUrl("ws://your.socket.url")
        .setSocketListener(new SocketManager.SocketListener() {
            @Override
            public void onConnected() {
                Log.d("SocketTest", "连接成功");
            }

            @Override
            public void onDisconnected(String reason) {
                Log.d("SocketTest", "断开连接: " + reason);
            }

            @Override
            public void onMessage(String text) {
                Log.d("SocketTest", "收到消息: " + text);
            }

            @Override
            public void onMessage(ByteString bytes) {
                Log.d("SocketTest", "收到二进制消息");
            }

            @Override
            public void onError(String error) {
                Log.e("SocketTest", "错误: " + error);
            }
        })
        .connect();

功能特点

支持 MVVM + LiveData ,UI 层只观察数据变化✅ 支持独立使用 ,通过 SocketListener 回调✅ 自动重连文本 & 二进制消息 都支持✅ 连接状态、错误信息 都有 LiveData 通知


✅ 这样你的 Socket 管理类就既能在 MVVM 架构中无缝使用,又能在普通 Activity / Service / 工具类中直接调用,非常灵活。

方式三封装。在上面的代码基础上,加上心跳检测机制,让 Socket 在长时间无消息时自动发送心跳包,防止被服务器断开。

SocketManager 中加上 心跳检测机制,这样长时间没收到服务器消息时,会自动发送心跳包保持连接,避免被服务器断开。

设计思路

  • 心跳间隔 :每隔一段时间(如 30 秒)发送一次固定格式的心跳包(比如 "ping"
  • 心跳超时检测:如果超过一定时间(如 60 秒)没收到服务器的任何消息,则认为连接已断开,触发重连
  • 使用 Handler 在主线程定时执行心跳任务
  • 收到任何消息(包括心跳响应)都会重置超时计时器带心跳检测的 SocketManager(支持 MVVM LiveData)

带心跳检测的 SocketManager(支持 MVVM LiveData)

复制代码
package com.nyw.mvvmmode.net.socket;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

/**
 * 基于 OkHttp WebSocket 的 Socket 连接管理类
 * 支持 MVVM + LiveData,也可独立使用,增加心跳检测机制
 */
public class SocketManager {
    private static final String TAG = "SocketManager";
    private static volatile SocketManager instance;

    private OkHttpClient okHttpClient;
    private WebSocket webSocket;
    private boolean isConnected = false;
    private String socketUrl;

    // 重连间隔
    private static final long RECONNECT_INTERVAL = 5000;

    // 心跳间隔(30秒)
    private static final long HEARTBEAT_INTERVAL = 30 * 1000;

    // 心跳超时时间(60秒)
    private static final long HEARTBEAT_TIMEOUT = 60 * 1000;

    // 心跳消息内容
    private static final String HEARTBEAT_MSG = "ping";

    // 独立使用的回调
    private SocketListener socketListener;

    // 用于 MVVM 的 LiveData
    private final MutableLiveData<String> messageLiveData = new MutableLiveData<>();
    private final MutableLiveData<ByteString> binaryLiveData = new MutableLiveData<>();
    private final MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
    private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();

    // 心跳 & 超时检测 Handler
    private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            if (msg.what == 1) { // 心跳任务
                sendHeartbeat();
                // 发送心跳后,启动超时检测
                mainHandler.sendEmptyMessageDelayed(2, HEARTBEAT_TIMEOUT);
            } else if (msg.what == 2) { // 超时任务
                Log.e(TAG, "心跳超时,断开连接并尝试重连");
                disconnect();
                reconnect();
            }
            return true;
        }
    });

    private SocketManager() {
        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
                .build();
    }

    public static SocketManager getInstance() {
        if (instance == null) {
            synchronized (SocketManager.class) {
                if (instance == null) {
                    instance = new SocketManager();
                }
            }
        }
        return instance;
    }

    public void setSocketUrl(String url) {
        this.socketUrl = url;
    }

    public void connect() {
        if (isConnected || socketUrl == null) return;

        Request request = new Request.Builder().url(socketUrl).build();
        webSocket = okHttpClient.newWebSocket(request, webSocketListener);
        isConnected = true;
    }

    public void disconnect() {
        if (webSocket != null) {
            webSocket.close(1000, "主动断开连接");
            webSocket = null;
        }
        isConnected = false;
        stopHeartbeat();
    }

    public void sendMessage(String message) {
        if (webSocket != null && isConnected) {
            webSocket.send(message);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    public void sendMessage(ByteString byteString) {
        if (webSocket != null && isConnected) {
            webSocket.send(byteString);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    public boolean isConnected() {
        return isConnected;
    }

    // ====== 独立使用的回调 ======
    public void setSocketListener(SocketListener listener) {
        this.socketListener = listener;
    }

    // ====== MVVM LiveData 接口 ======
    public LiveData<String> getMessageLiveData() {
        return messageLiveData;
    }

    public LiveData<ByteString> getBinaryLiveData() {
        return binaryLiveData;
    }

    public LiveData<Boolean> getConnectionLiveData() {
        return connectionLiveData;
    }

    public LiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    /**
     * 发送心跳包
     */
    private void sendHeartbeat() {
        if (isConnected) {
            Log.d(TAG, "发送心跳包: " + HEARTBEAT_MSG);
            sendMessage(HEARTBEAT_MSG);
        }
    }

    /**
     * 启动心跳任务
     */
    private void startHeartbeat() {
        stopHeartbeat();
        mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
    }

    /**
     * 停止心跳任务
     */
    private void stopHeartbeat() {
        mainHandler.removeMessages(1);
        mainHandler.removeMessages(2);
    }

    /**
     * 重置心跳计时器(收到任何消息时调用)
     */
    private void resetHeartbeat() {
        stopHeartbeat();
        mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
    }

    /**
     * WebSocket 回调
     */
    private final WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "Socket 连接成功");
            isConnected = true;

            // 回调
            if (socketListener != null) socketListener.onConnected();
            connectionLiveData.postValue(true);

            // 启动心跳
            startHeartbeat();
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
            super.onMessage(webSocket, text);
            Log.d(TAG, "收到文本消息: " + text);

            // 重置心跳计时器
            resetHeartbeat();

            // 回调
            if (socketListener != null) socketListener.onMessage(text);
            messageLiveData.postValue(text);
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
            super.onMessage(webSocket, bytes);
            Log.d(TAG, "收到二进制消息: " + bytes.hex());

            // 重置心跳计时器
            resetHeartbeat();

            // 回调
            if (socketListener != null) socketListener.onMessage(bytes);
            binaryLiveData.postValue(bytes);
        }

        @Override
        public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosing(webSocket, code, reason);
            Log.d(TAG, "Socket 正在关闭: " + reason);
            isConnected = false;

            if (socketListener != null) socketListener.onDisconnected(reason);
            connectionLiveData.postValue(false);

            stopHeartbeat();
        }

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "Socket 已关闭: " + reason);
            isConnected = false;

            if (socketListener != null) socketListener.onDisconnected(reason);
            connectionLiveData.postValue(false);

            stopHeartbeat();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "Socket 连接失败: " + t.getMessage());
            isConnected = false;

            if (socketListener != null) socketListener.onError(t.getMessage());
            errorLiveData.postValue(t.getMessage());

            stopHeartbeat();
            // 自动重连
            reconnect();
        }
    };

    /**
     * 自动重连
     */
    private void reconnect() {
        if (!isConnected) {
            new Thread(() -> {
                try {
                    Thread.sleep(RECONNECT_INTERVAL);
                    connect();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    /**
     * 独立使用的回调接口
     */
    public interface SocketListener {
        void onConnected();
        void onDisconnected(String reason);
        void onMessage(String text);
        void onMessage(ByteString bytes);
        void onError(String error);
    }
}

心跳机制说明

  • 启动心跳 :在 onOpen 回调中调用 startHeartbeat()
  • 发送心跳 :每隔 HEARTBEAT_INTERVAL(30 秒)发送一次 "ping"
  • 超时检测 :如果超过 HEARTBEAT_TIMEOUT(60 秒)没有收到任何消息,就触发重连
  • 重置心跳:每次收到服务器消息(文本 / 二进制)都会重置计时器
  • 停止心跳 :连接断开或关闭时调用 stopHeartbeat()

MVVM 使用方式(不变)

java

运行

复制代码
viewModel.getMessageLiveData().observe(this, msg -> {
    Log.d("SocketActivity", "收到消息: " + msg);
});

单独使用方式(不变)

复制代码
SocketManager.getInstance()
        .setSocketUrl("ws://your.socket.url")
        .setSocketListener(new SocketManager.SocketListener() {
            @Override
            public void onConnected() {
                Log.d("SocketTest", "连接成功");
            }
            @Override
            public void onDisconnected(String reason) {
                Log.d("SocketTest", "断开连接: " + reason);
            }
            @Override
            public void onMessage(String text) {
                Log.d("SocketTest", "收到消息: " + text);
            }
            @Override
            public void onMessage(ByteString bytes) {}
            @Override
            public void onError(String error) {}
        })
        .connect();

✅ 这样你的 SocketManager 现在支持:

  • MVVM + LiveData
  • 独立使用
  • 自动重连
  • 心跳检测

封装4,在加一个 "后台保活" 功能, 在 Service 中运行 Socket 连接,这样即使 App 退到后台,Socket 也能保持连接。当前的 SocketManager 基础上增加一个 后台保活功能 ,通过 Service 来运行 Socket 连接,这样即使 App 退到后台,也能保持长连接。

设计思路

  1. 创建一个 SocketService 继承自 Service
  2. SocketService 中持有 SocketManager 实例,并在 onCreate() 启动连接
  3. onDestroy() 断开连接
  4. 使用 startForeground() 提升 Service 优先级,减少被系统杀死的概率
  5. 在 Application 或 Activity 中启动 Service

Socket 保活 Service

复制代码
package com.nyw.mvvmmode.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import com.nyw.mvvmmode.MainActivity;
import com.nyw.mvvmmode.R;
import com.nyw.mvvmmode.net.socket.SocketManager;

/**
 * Socket 后台保活 Service
 */
public class SocketService extends android.app.Service {
    private static final String TAG = "SocketService";
    private static final int NOTIFICATION_ID = 1001;
    private static final String CHANNEL_ID = "socket_channel";

    private SocketManager socketManager;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "SocketService onCreate");

        // 获取 SocketManager 实例
        socketManager = SocketManager.getInstance();
        socketManager.setSocketUrl("ws://your.socket.url");

        // 启动 Socket 连接
        socketManager.connect();

        // 启动前台服务,提高优先级
        startForeground(NOTIFICATION_ID, createNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "SocketService onStartCommand");
        return START_STICKY; // 服务被杀死后自动重启
    }

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

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

        // 断开 Socket 连接
        if (socketManager != null) {
            socketManager.disconnect();
        }
    }

    /**
     * 创建前台通知
     */
    private Notification createNotification() {
        // 创建通知渠道(Android 8.0+ 必须)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "Socket 保活",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }

        // 点击通知回到 MainActivity
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                intent,
                PendingIntent.FLAG_IMMUTABLE
        );

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Socket 连接中")
                .setContentText("保持后台实时通信")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .build();
    }
}

在 AndroidManifest.xml 注册 Service

复制代码
<service
    android:name=".service.SocketService"
    android:foregroundServiceType="dataSync"
    android:exported="false" />

启动 Service 的方式

从 Application 启动(推荐)

复制代码
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 启动 Socket 保活服务
        Intent intent = new Intent(this, SocketService.class);
        startForegroundService(intent);
    }
}

从 Activity 启动

复制代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(new Intent(this, SocketService.class));
} else {
    startService(new Intent(this, SocketService.class));
}

后台保活原理

  • 前台服务 :通过 startForeground() 显示一个常驻通知,提高 Service 优先级
  • START_STICKY:Service 被系统杀死后会尝试重启
  • 数据同步类型foregroundServiceType="dataSync" 表示这是数据同步服务,符合 Android 10+ 后台限制要求

5. 注意事项

  • Android 8.0+ 必须使用 startForegroundService() 并在 5 秒内调用 startForeground()
  • 通知栏图标和文字是必须的,否则会崩溃
  • 某些手机厂商(如华为、小米)可能需要手动开启后台权限
  • 长时间后台运行仍可能受到系统电量优化限制

✅ 这样你的 SocketManager 现在支持:

  • MVVM + LiveData
  • 独立使用
  • 自动重连
  • 心跳检测
  • 后台保活(Service)

封装5,把 SocketService 和 SocketManager 整合得更紧密,比如在 Service 中自动管理连接状态,并且在 UI 中也能通过 LiveData 观察连接和消息。

SocketServiceSocketManager 整合得更紧密,这样 Service 会负责 Socket 的生命周期管理,UI 层依然可以通过 LiveData 观察连接状态和消息,即使 Activity 退出,Socket 也能在后台保持连接。

设计思路

  1. SocketManager 保留单例模式,但不再自己管理连接,而是提供连接 / 断开方法
  2. SocketService 启动时调用 SocketManager.connect(),销毁时调用 SocketManager.disconnect()
  3. Service 与 Manager 之间通过 回调 通信
  4. UI 层通过 SocketManager 的 LiveData 观察数据,不直接依赖 Service
  5. 后台保活通过 startForeground() 实现

整合后的 SocketManager(支持 LiveData + 回调)

复制代码
package com.nyw.mvvmmode.net.socket;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

/**
 * 基于 OkHttp WebSocket 的 Socket 连接管理类
 * 支持 MVVM + LiveData,也可独立使用,带心跳检测
 */
public class SocketManager {
    private static final String TAG = "SocketManager";
    private static volatile SocketManager instance;

    private OkHttpClient okHttpClient;
    private WebSocket webSocket;
    private boolean isConnected = false;
    private String socketUrl;

    // 重连间隔
    private static final long RECONNECT_INTERVAL = 5000;

    // 心跳间隔(30秒)
    private static final long HEARTBEAT_INTERVAL = 30 * 1000;

    // 心跳超时时间(60秒)
    private static final long HEARTBEAT_TIMEOUT = 60 * 1000;

    // 心跳消息内容
    private static final String HEARTBEAT_MSG = "ping";

    // 独立使用的回调
    private SocketListener socketListener;

    // 用于 MVVM 的 LiveData
    private final MutableLiveData<String> messageLiveData = new MutableLiveData<>();
    private final MutableLiveData<ByteString> binaryLiveData = new MutableLiveData<>();
    private final MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
    private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();

    // 心跳 & 超时检测 Handler
    private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            if (msg.what == 1) { // 心跳任务
                sendHeartbeat();
                // 发送心跳后,启动超时检测
                mainHandler.sendEmptyMessageDelayed(2, HEARTBEAT_TIMEOUT);
            } else if (msg.what == 2) { // 超时任务
                Log.e(TAG, "心跳超时,断开连接并尝试重连");
                disconnect();
                reconnect();
            }
            return true;
        }
    });

    private SocketManager() {
        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
                .build();
    }

    public static SocketManager getInstance() {
        if (instance == null) {
            synchronized (SocketManager.class) {
                if (instance == null) {
                    instance = new SocketManager();
                }
            }
        }
        return instance;
    }

    public void setSocketUrl(String url) {
        this.socketUrl = url;
    }

    public void connect() {
        if (isConnected || socketUrl == null) return;

        Request request = new Request.Builder().url(socketUrl).build();
        webSocket = okHttpClient.newWebSocket(request, webSocketListener);
        isConnected = true;
    }

    public void disconnect() {
        if (webSocket != null) {
            webSocket.close(1000, "主动断开连接");
            webSocket = null;
        }
        isConnected = false;
        stopHeartbeat();
    }

    public void sendMessage(String message) {
        if (webSocket != null && isConnected) {
            webSocket.send(message);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    public void sendMessage(ByteString byteString) {
        if (webSocket != null && isConnected) {
            webSocket.send(byteString);
        } else {
            Log.e(TAG, "Socket 未连接,无法发送消息");
        }
    }

    public boolean isConnected() {
        return isConnected;
    }

    // ====== 独立使用的回调 ======
    public void setSocketListener(SocketListener listener) {
        this.socketListener = listener;
    }

    // ====== MVVM LiveData 接口 ======
    public LiveData<String> getMessageLiveData() {
        return messageLiveData;
    }

    public LiveData<ByteString> getBinaryLiveData() {
        return binaryLiveData;
    }

    public LiveData<Boolean> getConnectionLiveData() {
        return connectionLiveData;
    }

    public LiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    /**
     * 发送心跳包
     */
    private void sendHeartbeat() {
        if (isConnected) {
            Log.d(TAG, "发送心跳包: " + HEARTBEAT_MSG);
            sendMessage(HEARTBEAT_MSG);
        }
    }

    /**
     * 启动心跳任务
     */
    private void startHeartbeat() {
        stopHeartbeat();
        mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
    }

    /**
     * 停止心跳任务
     */
    private void stopHeartbeat() {
        mainHandler.removeMessages(1);
        mainHandler.removeMessages(2);
    }

    /**
     * 重置心跳计时器(收到任何消息时调用)
     */
    private void resetHeartbeat() {
        stopHeartbeat();
        mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
    }

    /**
     * WebSocket 回调
     */
    private final WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "Socket 连接成功");
            isConnected = true;

            // 回调
            if (socketListener != null) socketListener.onConnected();
            connectionLiveData.postValue(true);

            // 启动心跳
            startHeartbeat();
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
            super.onMessage(webSocket, text);
            Log.d(TAG, "收到文本消息: " + text);

            // 重置心跳计时器
            resetHeartbeat();

            // 回调
            if (socketListener != null) socketListener.onMessage(text);
            messageLiveData.postValue(text);
        }

        @Override
        public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
            super.onMessage(webSocket, bytes);
            Log.d(TAG, "收到二进制消息: " + bytes.hex());

            // 重置心跳计时器
            resetHeartbeat();

            // 回调
            if (socketListener != null) socketListener.onMessage(bytes);
            binaryLiveData.postValue(bytes);
        }

        @Override
        public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosing(webSocket, code, reason);
            Log.d(TAG, "Socket 正在关闭: " + reason);
            isConnected = false;

            if (socketListener != null) socketListener.onDisconnected(reason);
            connectionLiveData.postValue(false);

            stopHeartbeat();
        }

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "Socket 已关闭: " + reason);
            isConnected = false;

            if (socketListener != null) socketListener.onDisconnected(reason);
            connectionLiveData.postValue(false);

            stopHeartbeat();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "Socket 连接失败: " + t.getMessage());
            isConnected = false;

            if (socketListener != null) socketListener.onError(t.getMessage());
            errorLiveData.postValue(t.getMessage());

            stopHeartbeat();
            // 自动重连
            reconnect();
        }
    };

    /**
     * 自动重连
     */
    private void reconnect() {
        if (!isConnected) {
            new Thread(() -> {
                try {
                    Thread.sleep(RECONNECT_INTERVAL);
                    connect();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    /**
     * 独立使用的回调接口
     */
    public interface SocketListener {
        void onConnected();
        void onDisconnected(String reason);
        void onMessage(String text);
        void onMessage(ByteString bytes);
        void onError(String error);
    }
}

整合后的 SocketService

复制代码
package com.nyw.mvvmmode.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.Observer;

import com.nyw.mvvmmode.MainActivity;
import com.nyw.mvvmmode.R;
import com.nyw.mvvmmode.net.socket.SocketManager;

/**
 * Socket 后台保活 Service(与 SocketManager 紧密整合)
 */
public class SocketService extends android.app.Service {
    private static final String TAG = "SocketService";
    private static final int NOTIFICATION_ID = 1001;
    private static final String CHANNEL_ID = "socket_channel";

    private SocketManager socketManager;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "SocketService onCreate");

        // 获取 SocketManager 实例
        socketManager = SocketManager.getInstance();
        socketManager.setSocketUrl("ws://your.socket.url");

        // 观察连接状态变化(可用于更新通知)
        socketManager.getConnectionLiveData().observeForever(connectionObserver);

        // 启动 Socket 连接
        socketManager.connect();

        // 启动前台服务,提高优先级
        startForeground(NOTIFICATION_ID, createNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "SocketService onStartCommand");
        return START_STICKY; // 服务被杀死后自动重启
    }

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

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

        // 移除观察者
        socketManager.getConnectionLiveData().removeObserver(connectionObserver);

        // 断开 Socket 连接
        socketManager.disconnect();
    }

    /**
     * 创建前台通知
     */
    private Notification createNotification() {
        // 创建通知渠道(Android 8.0+ 必须)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "Socket 保活",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }

        // 点击通知回到 MainActivity
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                intent,
                PendingIntent.FLAG_IMMUTABLE
        );

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Socket 连接中")
                .setContentText("保持后台实时通信")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .build();
    }

    /**
     * 连接状态观察者
     */
    private final Observer<Boolean> connectionObserver = isConnected -> {
        if (isConnected != null && isConnected) {
            Log.d(TAG, "Socket 连接成功(Service 观察到)");
            // 可更新通知内容
        } else {
            Log.d(TAG, "Socket 已断开(Service 观察到)");
        }
    };
}

启动 Service

复制代码
// 在 Application 或 Activity 中启动
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(new Intent(context, SocketService.class));
} else {
    context.startService(new Intent(context, SocketService.class));
}

UI 层通过 ViewModel 观察

复制代码
public class SocketViewModel extends AndroidViewModel {
    private final SocketManager socketManager;

    public SocketViewModel(@NonNull Application application) {
        super(application);
        socketManager = SocketManager.getInstance();
    }

    public LiveData<String> getMessageLiveData() {
        return socketManager.getMessageLiveData();
    }

    public LiveData<Boolean> getConnectionLiveData() {
        return socketManager.getConnectionLiveData();
    }

    public void sendMessage(String msg) {
        socketManager.sendMessage(msg);
    }
}

整合后的优势

Service 负责 Socket 生命周期 (启动 / 销毁 / 重连)✅ UI 层完全解耦 ,只通过 LiveData 观察数据✅ 后台保活 ,通过前台服务提高优先级✅ 心跳检测 保持连接稳定✅ 自动重连 机制✅ 支持独立使用(不依赖 MVVM)


✅ 这样你的长连接模块就非常完善了,可以直接在项目中使用,而且结构清晰,Service 负责后台运行,Manager 负责业务逻辑,UI 层只管展示数据。

封装6,再加上 "网络状态变化自动重连" 功能, 当设备网络从无到有时,自动重新连接 Socket。SocketManager 增加一个 网络状态变化自动重连 的功能,这样当设备从无网络切换到有网络时,Socket 会自动尝试重新连接,保证长连接的稳定性。

设计思路

  1. 使用 ConnectivityManager.NetworkCallback 监听网络状态变化(Android 7.0+ 推荐)
  2. 当网络从 无连接 → 有连接 时,如果 Socket 当前未连接,则调用 connect()
  3. 当网络从 有连接 → 无连接 时,断开 Socket 连接
  4. SocketManager 初始化时注册网络监听,在销毁时取消监听
  5. 兼容旧版本系统(可使用广播监听网络变化)

网络状态监听工具类

复制代码
package com.nyw.mvvmmode.utils;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;

/**
 * 网络工具类(判断网络状态 + 监听网络变化)
 */
public class NetworkUtils {

    /**
     * 判断是否有网络连接
     */
    public static boolean isNetworkConnected(Context context) {
        if (context == null) return false;

        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return false;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = cm.getActiveNetwork();
            if (network == null) return false;

            NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
            return capabilities != null && (
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
            );
        } else {
            NetworkInfo info = cm.getActiveNetworkInfo();
            return info != null && info.isConnected();
        }
    }

    /**
     * 注册网络变化监听(Android 7.0+)
     */
    public static void registerNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (cm != null) {
                cm.registerDefaultNetworkCallback(callback);
            }
        }
    }

    /**
     * 取消网络变化监听
     */
    public static void unregisterNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (cm != null) {
                cm.unregisterNetworkCallback(callback);
            }
        }
    }
}

修改 SocketManager,增加网络监听

SocketManager 中添加以下代码:

复制代码
// 在 SocketManager 中添加
private ConnectivityManager.NetworkCallback networkCallback;

private SocketManager() {
    okHttpClient = new OkHttpClient.Builder()
            .readTimeout(0, TimeUnit.MILLISECONDS)
            .build();

    // 初始化网络监听
    initNetworkCallback();
}

/**
 * 初始化网络监听
 */
private void initNetworkCallback() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                super.onAvailable(network);
                Log.d(TAG, "网络已恢复,尝试重连 Socket");
                if (!isConnected()) {
                    connect();
                }
            }

            @Override
            public void onLost(Network network) {
                super.onLost(network);
                Log.d(TAG, "网络已断开,关闭 Socket");
                if (isConnected()) {
                    disconnect();
                }
            }
        };
    }
}

/**
 * 注册网络监听(建议在 Application 初始化时调用)
 */
public void registerNetworkListener(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        NetworkUtils.registerNetworkCallback(context, networkCallback);
    }
}

/**
 * 取消网络监听
 */
public void unregisterNetworkListener(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        NetworkUtils.unregisterNetworkCallback(context, networkCallback);
    }
}

最终完整代码集合如下

完整的 Socket 长连接管理代码 ,支持到 Android 16(Android 4.1),包含我们之前讨论的所有功能:

  • MVVM + LiveData 支持
  • 自动重连(连接失败 / 断开)
  • 心跳检测(定时发送 ping)
  • 后台保活(前台服务)
  • 网络状态变化自动重连
  • 连接状态持久化(SharedPreferences)

1. 目录结构

plaintext

复制代码
app/src/main/java/com/yourpackage/socket/
├── SocketManager.java          // 核心管理类(单例)
├── SocketService.java          // 前台服务保活
├── NetworkUtils.java           // 网络工具类(判断网络状态 + 监听网络变化)
├── MyApplication.java          // 全局 Application
└── viewmodel/
    └── SocketViewModel.java    // ViewModel 示例

2. NetworkUtils.java(网络工具类)

复制代码
package com.yourpackage.socket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;

/**
 * 网络工具类
 * 支持 Android 16+
 */
public class NetworkUtils {

    /**
     * 判断是否有网络连接
     */
    public static boolean isNetworkConnected(Context context) {
        if (context == null) return false;

        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return false;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = cm.getActiveNetwork();
            if (network == null) return false;

            NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
            return capabilities != null && (
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
            );
        } else {
            NetworkInfo info = cm.getActiveNetworkInfo();
            return info != null && info.isConnected();
        }
    }

    /**
     * 注册网络变化监听(Android 7.0+)
     */
    public static void registerNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (cm != null) {
                cm.registerDefaultNetworkCallback(callback);
            }
        }
    }

    /**
     * 取消网络变化监听
     */
    public static void unregisterNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (cm != null) {
                cm.unregisterNetworkCallback(callback);
            }
        }
    }
}

3. SocketManager.java(核心管理类)

复制代码
package com.yourpackage.socket;

import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Network;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

/**
 * Socket 长连接管理类(单例模式)
 * 功能:
 * 1. 自动重连
 * 2. 心跳检测
 * 3. 网络状态监听
 * 4. 连接状态持久化
 * 5. LiveData 数据分发
 */
public class SocketManager {
    private static final String TAG = "SocketManager";
    private static final String PREF_NAME = "socket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_SOCKET_URL = "socket_url";

    private static SocketManager instance;

    private OkHttpClient okHttpClient;
    private WebSocket webSocket;
    private String socketUrl;
    private boolean isConnected = false;

    private MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
    private MutableLiveData<String> messageLiveData = new MutableLiveData<>();
    private MutableLiveData<String> errorLiveData = new MutableLiveData<>();

    private Handler mainHandler = new Handler(Looper.getMainLooper());
    private Runnable heartbeatRunnable;
    private static final long HEARTBEAT_INTERVAL = 10 * 1000; // 10秒

    private ConnectivityManager.NetworkCallback networkCallback;

    private SocketManager() {
        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();

        initNetworkCallback();
        restoreConnectionState();
    }

    public static synchronized SocketManager getInstance() {
        if (instance == null) {
            instance = new SocketManager();
        }
        return instance;
    }

    public void setSocketUrl(String url) {
        this.socketUrl = url;
    }

    public LiveData<Boolean> getConnectionLiveData() {
        return connectionLiveData;
    }

    public LiveData<String> getMessageLiveData() {
        return messageLiveData;
    }

    public LiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    public boolean isConnected() {
        return isConnected;
    }

    /**
     * 连接 WebSocket
     */
    public void connect() {
        if (isConnected || socketUrl == null) return;

        Request request = new Request.Builder().url(socketUrl).build();
        webSocket = okHttpClient.newWebSocket(request, webSocketListener);
        updateConnectionState(true);
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        if (webSocket != null) {
            webSocket.close(1000, "主动断开连接");
            webSocket = null;
        }
        updateConnectionState(false);
        stopHeartbeat();
    }

    /**
     * 发送消息
     */
    public void sendMessage(String text) {
        if (webSocket != null) {
            webSocket.send(text);
        }
    }

    /**
     * 初始化网络监听
     */
    private void initNetworkCallback() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            networkCallback = new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    super.onAvailable(network);
                    if (!isConnected()) {
                        connect();
                    }
                }

                @Override
                public void onLost(Network network) {
                    super.onLost(network);
                    if (isConnected()) {
                        disconnect();
                    }
                }
            };
        }
    }

    /**
     * 注册网络监听
     */
    public void registerNetworkListener(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            NetworkUtils.registerNetworkCallback(context, networkCallback);
        }
    }

    /**
     * 取消网络监听
     */
    public void unregisterNetworkListener(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            NetworkUtils.unregisterNetworkCallback(context, networkCallback);
        }
    }

    /**
     * 保存连接状态
     */
    private void saveConnectionState() {
        Context context = MyApplication.getContext();
        if (context == null) return;

        SharedPreferences sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_SOCKET_URL, socketUrl);
        editor.apply();
    }

    /**
     * 恢复连接状态
     */
    private void restoreConnectionState() {
        Context context = MyApplication.getContext();
        if (context == null) return;

        SharedPreferences sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        socketUrl = sp.getString(KEY_SOCKET_URL, null);

        if (isConnected && socketUrl != null) {
            connect();
        }
    }

    /**
     * 更新连接状态
     */
    private void updateConnectionState(boolean connected) {
        isConnected = connected;
        connectionLiveData.postValue(connected);
        saveConnectionState();
    }

    /**
     * 启动心跳
     */
    private void startHeartbeat() {
        stopHeartbeat();
        heartbeatRunnable = new Runnable() {
            @Override
            public void run() {
                if (webSocket != null) {
                    webSocket.send(ByteString.EMPTY);
                }
                mainHandler.postDelayed(this, HEARTBEAT_INTERVAL);
            }
        };
        mainHandler.postDelayed(heartbeatRunnable, HEARTBEAT_INTERVAL);
    }

    /**
     * 停止心跳
     */
    private void stopHeartbeat() {
        if (heartbeatRunnable != null) {
            mainHandler.removeCallbacks(heartbeatRunnable);
            heartbeatRunnable = null;
        }
    }

    /**
     * WebSocket 回调监听
     */
    private WebSocketListener webSocketListener = new WebSocketListener() {
        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            super.onOpen(webSocket, response);
            updateConnectionState(true);
            startHeartbeat();
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
            super.onMessage(webSocket, text);
            messageLiveData.postValue(text);
        }

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            super.onMessage(webSocket, bytes);
            // 处理二进制消息
        }

        @Override
        public void onClosed(WebSocket webSocket, int code, String reason) {
            super.onClosed(webSocket, code, reason);
            updateConnectionState(false);
            stopHeartbeat();
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            errorLiveData.postValue(t.getMessage());
            updateConnectionState(false);
            stopHeartbeat();
            // 自动重连
            mainHandler.postDelayed(() -> connect(), 5000);
        }
    };
}

4. SocketService.java(前台服务保活)

复制代码
package com.yourpackage.socket;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;

import androidx.core.app.NotificationCompat;

/**
 * 前台服务保活
 */
public class SocketService extends Service {
    private static final int NOTIFICATION_ID = 1001;
    private static final String CHANNEL_ID = "socket_channel";

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(NOTIFICATION_ID, createNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 确保Socket连接
        if (!SocketManager.getInstance().isConnected()) {
            SocketManager.getInstance().connect();
        }
        return START_STICKY;
    }

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

    /**
     * 创建前台通知
     */
    private Notification createNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "Socket连接",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }

        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                intent,
                PendingIntent.FLAG_IMMUTABLE
        );

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Socket连接中")
                .setContentText("保持后台连接")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .build();
    }
}

5. MyApplication.java(全局 Application)

java

运行

复制代码
package com.yourpackage.socket;

import android.app.Application;
import android.content.Context;

/**
 * 全局 Application
 */
public class MyApplication extends Application {
    private static Context context;

    public static Context getContext() {
        return context;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = this;

        // 初始化SocketManager
        SocketManager.getInstance()
                .setSocketUrl("ws://your.socket.url")
                .registerNetworkListener(this);

        // 启动Socket保活服务
        startSocketService();
    }

    /**
     * 启动Socket服务
     */
    private void startSocketService() {
        Intent intent = new Intent(this, SocketService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent);
        } else {
            startService(intent);
        }
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        SocketManager.getInstance().unregisterNetworkListener(this);
    }
}

6. SocketViewModel.java(ViewModel 示例)

java

运行

复制代码
package com.yourpackage.socket.viewmodel;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import com.yourpackage.socket.SocketManager;

/**
 * Socket ViewModel
 */
public class SocketViewModel extends AndroidViewModel {
    private SocketManager socketManager;

    public SocketViewModel(@NonNull Application application) {
        super(application);
        socketManager = SocketManager.getInstance();
    }

    public LiveData<Boolean> getConnectionState() {
        return socketManager.getConnectionLiveData();
    }

    public LiveData<String> getMessages() {
        return socketManager.getMessageLiveData();
    }

    public LiveData<String> getErrors() {
        return socketManager.getErrorLiveData();
    }

    public void sendMessage(String message) {
        socketManager.sendMessage(message);
    }
}

7. 权限配置(AndroidManifest.xml)

xml

复制代码
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application
    android:name=".socket.MyApplication"
    ...>
    
    <service
        android:name=".socket.SocketService"
        android:foregroundServiceType="dataSync"
        android:exported="false" />
        
    <!-- MainActivity -->
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

这样你就有了一套完整的 Socket 长连接解决方案

  • 支持 Android 16+
  • 自动重连
  • 心跳检测
  • 网络状态监听
  • 连接状态持久化
  • MVVM + LiveData 架构
  • 前台服务保活
相关推荐
linuxxx1102 小时前
ajax回调钩子的使用简介
okhttp
夜晚中的人海3 小时前
【C++】异常介绍
android·java·c++
蜀中廖化8 小时前
Android Studio 导入 opencv
android·opencv·android studio
奋斗的小鹰8 小时前
ASM Bytecode Viewer 插件查看kotlin和java文件的字节码
android·kotlin·asm
欢喜躲在眉梢里10 小时前
mysql中的日志
android·运维·数据库·mysql·adb·日志·mysql日志
路上^_^11 小时前
安卓基础组件019-引导页布局001
android·安卓
梦终剧12 小时前
【Android之路】UI消息循环机制
android·ui
zh_xuan12 小时前
Android android.util.LruCache源码阅读
android·源码阅读·lrucache
梦终剧12 小时前
【Android之路】安卓资源与编译初步
android