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 messageLiveData = new MutableLiveData<>();
    private final MutableLiveData binaryLiveData = new MutableLiveData<>();
    private final MutableLiveData connectionLiveData = new MutableLiveData<>();
    private final MutableLiveData 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 getMessageLiveData() {
        return messageLiveData;
    }
    public LiveData getBinaryLiveData() {
        return binaryLiveData;
    }
    public LiveData getConnectionLiveData() {
        return connectionLiveData;
    }
    public LiveData 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 getMessageLiveData() {
        return socketManager.getMessageLiveData();
    }
    public LiveData getConnectionLiveData() {
        return socketManager.getConnectionLiveData();
    }
    public LiveData 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 messageLiveData = new MutableLiveData<>();
    private final MutableLiveData binaryLiveData = new MutableLiveData<>();
    private final MutableLiveData connectionLiveData = new MutableLiveData<>();
    private final MutableLiveData 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 getMessageLiveData() {
        return messageLiveData;
    }
    public LiveData getBinaryLiveData() {
        return binaryLiveData;
    }
    public LiveData getConnectionLiveData() {
        return connectionLiveData;
    }
    public LiveData 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 的方式

从 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 观察连接和消息。

把 SocketService 和 SocketManager 整合得更紧密,这样 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 messageLiveData = new MutableLiveData<>();
    private final MutableLiveData binaryLiveData = new MutableLiveData<>();
    private final MutableLiveData connectionLiveData = new MutableLiveData<>();
    private final MutableLiveData 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 getMessageLiveData() {
        return messageLiveData;
    }
    public LiveData getBinaryLiveData() {
        return binaryLiveData;
    }
    public LiveData getConnectionLiveData() {
        return connectionLiveData;
    }
    public LiveData 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 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 getMessageLiveData() {
        return socketManager.getMessageLiveData();
    }
    public LiveData 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 connectionLiveData = new MutableLiveData<>();
    private MutableLiveData messageLiveData = new MutableLiveData<>();
    private MutableLiveData 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 getConnectionLiveData() {
        return connectionLiveData;
    }
    public LiveData getMessageLiveData() {
        return messageLiveData;
    }
    public LiveData 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 getConnectionState() {
        return socketManager.getConnectionLiveData();
    }
    public LiveData getMessages() {
        return socketManager.getMessageLiveData();
    }
    public LiveData getErrors() {
        return socketManager.getErrorLiveData();
    }
    public void sendMessage(String message) {
        socketManager.sendMessage(message);
    }
}

7. 权限配置(AndroidManifest.xml)

xml





    
    
    
        
            
            
        
    

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

  • 支持 Android 16+
  • 自动重连
  • 心跳检测
  • 网络状态监听
  • 连接状态持久化
  • MVVM + LiveData 架构
  • 前台服务保活

posted on 2025-10-26 20:10  ljbguanli  阅读(0)  评论(0)    收藏  举报