android 基于okhttp 封装一个websocket管理模块,方便开发和使用

基于 OkHttp 封装一个 WebSocketSocket 管理模块 ,这个模块将支持 Android 16(API 16) 及以上版本,并且功能上扩展日常开发常用的功能,并比如自动重连、心跳检测、连接状态持久化、消息回调等,并且所有代码都会有详细的中文注解,方便你在项目中直接使用和扩展。

🎯 模块设计目标

  1. 易用性:单例模式,一行代码即可连接 / 发送消息
  2. 稳定性:断线网自动重连、心跳检测、断线重连策略
  3. 可扩展性:支持自定义消息处理、连接监听
  4. 持久化:保存连接状态,App 重启后自动恢复
  5. 兼容性:兼容 Android 16+

📂 模块结构

plaintext

复制代码
socket/
├── WebSocketManager.java      // 核心管理类
├── WebSocketService.java      // 前台服务保活(可选)
├── NetworkUtils.java          // 网络工具类
├── WebSocketListener.java     // 回调接口
└── WebSocketConfig.java       // 配置类

1️⃣ WebSocketConfig.java(配置类)

复制代码
package com.example.socket;

/**
 * WebSocket 配置类
 */
public class WebSocketConfig {
    public String url;                 // WebSocket地址
    public long heartbeatInterval = 10_000; // 心跳间隔(毫秒)
    public long reconnectInterval = 5_000;  // 重连间隔(毫秒)
    public boolean autoReconnect = true;   // 是否自动重连
    public boolean needHeartbeat = true;   // 是否需要心跳
}

2️⃣ WebSocketCallback .java(回调接口)

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



import okio.ByteString;

/**
 * WebSocket 回调接口
 * 所有回调都在主线程执行
 */
public interface WebSocketCallback {

    /**
     * 连接成功
     */
    void onConnected();

    /**
     * 连接断开
     * @param reason 断开原因
     */
    void onDisconnected(String reason);

    /**
     * 收到文本消息
     * @param message 文本内容
     */
    void onMessage(String message);

    /**
     * 收到二进制消息
     * @param bytes 二进制数据
     */
    void onMessage(ByteString bytes);

    /**
     * 连接发生错误
     * @param error 错误信息
     */
    void onError(String error);
}

3️⃣ NetworkUtils.java(网络工具类)

复制代码
package com.example.socket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/**
 * 网络工具类(兼容Android 16+)
 */
public class NetworkUtils {

    /**
     * 判断网络是否可用
     */
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return false;
        
        NetworkInfo[] info = cm.getAllNetworkInfo();
        if (info != null) {
            for (NetworkInfo networkInfo : info) {
                if (networkInfo.getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
        return false;
    }
}

4️⃣ WebSocketManager.java(核心管理类)

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


import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.concurrent.TimeUnit;

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

/**
 * WebSocket 管理类(单例模式)
 * 支持:
 * 1. 自动重连
 * 2. 心跳检测
 * 3. 连接状态持久化
 * 4. 消息回调
 * 5. 兼容 Android 16+
 */
public class WebSocketManager {
    private static final String TAG = "WebSocketManager";
    private static final String PREF_NAME = "websocket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_URL = "ws_url";

    private static volatile WebSocketManager instance;

    private OkHttpClient client;
    private WebSocket webSocket;
    private WebSocketConfig config;
    private WebSocketCallback callback;
    private boolean isConnected;
    private Handler mainHandler;
    private Runnable heartbeatTask;
    private Runnable reconnectTask;

    private Context appContext;

    private WebSocketManager() {
        mainHandler = new Handler(Looper.getMainLooper());
        client = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();
    }

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

    /**
     * 初始化 WebSocket
     */
    public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        restoreConnectionState();

        if (isConnected && config.url != null) {
            connect();
        }
    }

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

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
            Log.e(TAG, "网络不可用,无法连接WebSocket");
            if (callback != null) callback.onError("网络不可用");
            scheduleReconnect();
            return;
        }

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        Log.d(TAG, "正在连接 WebSocket: " + config.url);
    }

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

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

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

    /**
     * 启动心跳检测
     */
    private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        heartbeatTask = new Runnable() {
            @Override
            public void run() {
                if (isConnected && webSocket != null) {
                    webSocket.send(ByteString.EMPTY); // 发送空消息作为心跳
                    Log.d(TAG, "发送心跳包");
                }
                mainHandler.postDelayed(this, config.heartbeatInterval);
            }
        };
        mainHandler.postDelayed(heartbeatTask, config.heartbeatInterval);
    }

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

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        cancelReconnect();
        reconnectTask = new Runnable() {
            @Override
            public void run() {
                if (!isConnected) {
                    Log.d(TAG, "尝试重连 WebSocket");
                    connect();
                }
            }
        };
        mainHandler.postDelayed(reconnectTask, config.reconnectInterval);
    }

    /**
     * 取消重连
     */
    private void cancelReconnect() {
        if (reconnectTask != null) {
            mainHandler.removeCallbacks(reconnectTask);
            reconnectTask = null;
        }
    }

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

        if (callback != null) {
            if (connected) {
                callback.onConnected();
            } else {
                callback.onDisconnected("连接断开");
            }
        }
    }

    /**
     * 保存连接状态到本地
     */
    private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
    }

    /**
     * 从本地恢复连接状态
     */
    private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
            config.url = savedUrl;
        }
    }

    /**
     * 内部 WebSocket 监听(OkHttp 的)
     */
    private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "WebSocket 连接成功");
            updateConnectionState(true);
            startHeartbeat();
            cancelReconnect();
        }

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

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

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "WebSocket 关闭: " + reason);
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "WebSocket 连接失败: " + t.getMessage());
            if (callback != null) {
                callback.onError(t.getMessage());
            }
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }
    }
}

5️⃣ 使用示例

复制代码
// 1. 创建配置
WebSocketConfig config = new WebSocketConfig();
config.url = "ws://your.socket.url";
config.heartbeatInterval = 10_000; // 10秒
config.reconnectInterval = 5_000;  // 5秒
config.autoReconnect = true;
config.needHeartbeat = true;

// 2. 初始化并设置回调
WebSocketManager.getInstance().init(
        getApplicationContext(),
        config,
        new WebSocketCallback() {
            @Override
            public void onConnected() {
                Log.d("WS", "连接成功");
            }

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

            @Override
            public void onMessage(String message) {
                Log.d("WS", "收到文本消息: " + message);
            }

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

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

// 3. 连接
WebSocketManager.getInstance().connect();

// 4. 发送消息
WebSocketManager.getInstance().sendMessage("Hello WebSocket");

// 5. 断开连接(需要时)
// WebSocketManager.getInstance().disconnect();

6️⃣ 权限配置

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

✨ 功能亮点

  1. 兼容 Android 16+:使用兼容 API,适配低版本系统
  2. 自动重连:网络断开或连接失败时自动尝试重连
  3. 心跳检测:定时发送心跳包保持连接
  4. 连接状态持久化:保存连接状态,App 重启后自动恢复
  5. 消息回调:支持文本和二进制消息处理
  6. 灵活配置:可自定义心跳间隔、重连间隔等参数

✅ 这样,你就有了一个 完整可用的 WebSocket 封装模块,支持 Android 16+,代码中已经修正了之前的接口错误,

封装2、加上网络状态监听自动重连功能

支持 Android 16+ ,这样当网络从无到有时,WebSocket 会自动重新连接,不需要手动调用 connect()

🔹 修改 NetworkUtils.java

我们先增强网络工具类,让它支持网络状态监听(兼容低版本)。

复制代码
package com.example.socket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/**
 * 网络工具类(兼容 Android 16+)
 */
public class NetworkUtils {

    /**
     * 判断网络是否可用
     */
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager cm = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        if (cm == null) return false;

        // 低版本使用 getAllNetworkInfo()
        NetworkInfo[] info = cm.getAllNetworkInfo();
        if (info != null) {
            for (NetworkInfo networkInfo : info) {
                if (networkInfo.getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
        return false;
    }
}

🔹 在 WebSocketManager 中添加网络监听

我们在 WebSocketManager 中增加一个 registerNetworkListener() 方法,用广播监听网络变化(兼容 Android 16+)。

复制代码
package com.example.socket;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.concurrent.TimeUnit;

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

/**
 * WebSocket 管理类(单例模式)
 * 新增功能:网络状态监听自动重连
 */
public class WebSocketManager {
    private static final String TAG = "WebSocketManager";
    private static final String PREF_NAME = "websocket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_URL = "ws_url";

    private static volatile WebSocketManager instance;

    private OkHttpClient client;
    private WebSocket webSocket;
    private WebSocketConfig config;
    private WebSocketCallback callback;
    private boolean isConnected;
    private Handler mainHandler;
    private Runnable heartbeatTask;
    private Runnable reconnectTask;

    private Context appContext;
    private NetworkChangeReceiver networkReceiver;

    private WebSocketManager() {
        mainHandler = new Handler(Looper.getMainLooper());
        client = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();
    }

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

    /**
     * 初始化 WebSocket
     */
    public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        restoreConnectionState();

        // 注册网络监听
        registerNetworkListener();

        if (isConnected && config.url != null) {
            connect();
        }
    }

    /**
     * 注册网络状态监听广播
     */
    private void registerNetworkListener() {
        if (networkReceiver == null) {
            networkReceiver = new NetworkChangeReceiver();
            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
            appContext.registerReceiver(networkReceiver, filter);
        }
    }

    /**
     * 取消网络监听
     */
    public void unregisterNetworkListener() {
        if (networkReceiver != null) {
            try {
                appContext.unregisterReceiver(networkReceiver);
            } catch (Exception e) {
                e.printStackTrace();
            }
            networkReceiver = null;
        }
    }

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

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
            Log.e(TAG, "网络不可用,无法连接WebSocket");
            if (callback != null) callback.onError("网络不可用");
            scheduleReconnect();
            return;
        }

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        Log.d(TAG, "正在连接 WebSocket: " + config.url);
    }

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

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

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

    /**
     * 启动心跳检测
     */
    private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        heartbeatTask = new Runnable() {
            @Override
            public void run() {
                if (isConnected && webSocket != null) {
                    webSocket.send(ByteString.EMPTY); // 发送空消息作为心跳
                    Log.d(TAG, "发送心跳包");
                }
                mainHandler.postDelayed(this, config.heartbeatInterval);
            }
        };
        mainHandler.postDelayed(heartbeatTask, config.heartbeatInterval);
    }

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

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        cancelReconnect();
        reconnectTask = new Runnable() {
            @Override
            public void run() {
                if (!isConnected) {
                    Log.d(TAG, "尝试重连 WebSocket");
                    connect();
                }
            }
        };
        mainHandler.postDelayed(reconnectTask, config.reconnectInterval);
    }

    /**
     * 取消重连
     */
    private void cancelReconnect() {
        if (reconnectTask != null) {
            mainHandler.removeCallbacks(reconnectTask);
            reconnectTask = null;
        }
    }

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

        if (callback != null) {
            if (connected) {
                callback.onConnected();
            } else {
                callback.onDisconnected("连接断开");
            }
        }
    }

    /**
     * 保存连接状态到本地
     */
    private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
    }

    /**
     * 从本地恢复连接状态
     */
    private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
            config.url = savedUrl;
        }
    }

    /**
     * 内部 WebSocket 监听(OkHttp 的)
     */
    private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "WebSocket 连接成功");
            updateConnectionState(true);
            startHeartbeat();
            cancelReconnect();
        }

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

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

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "WebSocket 关闭: " + reason);
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "WebSocket 连接失败: " + t.getMessage());
            if (callback != null) {
                callback.onError(t.getMessage());
            }
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }
    }

    /**
     * 网络状态变化广播接收器
     */
    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                // 网络状态变化
                if (NetworkUtils.isNetworkAvailable(appContext)) {
                    Log.d(TAG, "网络已恢复,尝试连接WebSocket");
                    if (!isConnected && config.autoReconnect) {
                        connect();
                    }
                } else {
                    Log.d(TAG, "网络已断开");
                    if (isConnected) {
                        disconnect();
                    }
                }
            }
        }
    }
}

🔹 使用方式不变

java

运行

复制代码
WebSocketConfig config = new WebSocketConfig();
config.url = "ws://your.socket.url";
config.heartbeatInterval = 10_000;
config.reconnectInterval = 5_000;
config.autoReconnect = true;
config.needHeartbeat = true;

WebSocketManager.getInstance().init(
        getApplicationContext(),
        config,
        new WebSocketCallback() {
            @Override
            public void onConnected() {
                Log.d("WS", "连接成功");
            }

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

            @Override
            public void onMessage(String message) {
                Log.d("WS", "收到文本消息: " + message);
            }

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

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

// 第一次连接
WebSocketManager.getInstance().connect();

🔹 功能改进

现在 WebSocketManager 支持:✅ 自动重连 (连接失败 / 断开时)✅ 心跳检测 (保持连接)✅ 连接状态持久化 (App 重启自动恢复)✅ 网络状态监听 (网络恢复自动重连)✅ 兼容 Android 16+

封装3

现有的 WebSocket 封装模块 基础上增加 断线时消息缓存功能 ,这样当 WebSocket 未连接时,调用 sendMessage() 发送的消息会被缓存到本地队列,待网络恢复并重新连接成功后,会自动依次发送缓存的消息。

这个功能对于即时通讯、实时数据上报等场景非常有用。

🔹 修改 WebSocketManager.java

我会在 WebSocketManager 中添加一个 消息队列Queue)来缓存消息,并在连接成功后自动发送缓存的消息。

复制代码
package com.example.socket;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.Queue;

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

/**
 * WebSocket 管理类(单例模式)
 * 新增功能:
 * 1. 网络状态监听自动重连
 * 2. 断线消息缓存与自动发送
 */
public class WebSocketManager {
    private static final String TAG = "WebSocketManager";
    private static final String PREF_NAME = "websocket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_URL = "ws_url";

    private static volatile WebSocketManager instance;

    private OkHttpClient client;
    private WebSocket webSocket;
    private WebSocketConfig config;
    private WebSocketCallback callback;
    private boolean isConnected;
    private Handler mainHandler;
    private Runnable heartbeatTask;
    private Runnable reconnectTask;

    private Context appContext;
    private NetworkChangeReceiver networkReceiver;

    // 消息缓存队列(线程安全)
    private Queue<String> messageQueue = new ConcurrentLinkedQueue<>();
    private Queue<ByteString> byteMessageQueue = new ConcurrentLinkedQueue<>();

    private WebSocketManager() {
        mainHandler = new Handler(Looper.getMainLooper());
        client = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();
    }

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

    /**
     * 初始化 WebSocket
     */
    public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        restoreConnectionState();

        // 注册网络监听
        registerNetworkListener();

        if (isConnected && config.url != null) {
            connect();
        }
    }

    /**
     * 注册网络状态监听广播
     */
    private void registerNetworkListener() {
        if (networkReceiver == null) {
            networkReceiver = new NetworkChangeReceiver();
            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
            appContext.registerReceiver(networkReceiver, filter);
        }
    }

    /**
     * 取消网络监听
     */
    public void unregisterNetworkListener() {
        if (networkReceiver != null) {
            try {
                appContext.unregisterReceiver(networkReceiver);
            } catch (Exception e) {
                e.printStackTrace();
            }
            networkReceiver = null;
        }
    }

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

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
            Log.e(TAG, "网络不可用,无法连接WebSocket");
            if (callback != null) callback.onError("网络不可用");
            scheduleReconnect();
            return;
        }

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        Log.d(TAG, "正在连接 WebSocket: " + config.url);
    }

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

    /**
     * 发送文本消息(断线时会缓存)
     */
    public void sendMessage(String text) {
        if (isConnected && webSocket != null) {
            webSocket.send(text);
        } else {
            Log.e(TAG, "WebSocket 未连接,缓存消息: " + text);
            messageQueue.offer(text); // 加入缓存队列
        }
    }

    /**
     * 发送二进制消息(断线时会缓存)
     */
    public void sendMessage(ByteString bytes) {
        if (isConnected && webSocket != null) {
            webSocket.send(bytes);
        } else {
            Log.e(TAG, "WebSocket 未连接,缓存二进制消息");
            byteMessageQueue.offer(bytes); // 加入缓存队列
        }
    }

    /**
     * 发送缓存的消息
     */
    private void sendCachedMessages() {
        // 发送文本消息
        while (!messageQueue.isEmpty()) {
            String msg = messageQueue.poll();
            if (msg != null) {
                webSocket.send(msg);
                Log.d(TAG, "发送缓存文本消息: " + msg);
            }
        }

        // 发送二进制消息
        while (!byteMessageQueue.isEmpty()) {
            ByteString bytes = byteMessageQueue.poll();
            if (bytes != null) {
                webSocket.send(bytes);
                Log.d(TAG, "发送缓存二进制消息");
            }
        }
    }

    /**
     * 启动心跳检测
     */
    private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        heartbeatTask = new Runnable() {
            @Override
            public void run() {
                if (isConnected && webSocket != null) {
                    webSocket.send(ByteString.EMPTY); // 发送空消息作为心跳
                    Log.d(TAG, "发送心跳包");
                }
                mainHandler.postDelayed(this, config.heartbeatInterval);
            }
        };
        mainHandler.postDelayed(heartbeatTask, config.heartbeatInterval);
    }

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

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        cancelReconnect();
        reconnectTask = new Runnable() {
            @Override
            public void run() {
                if (!isConnected) {
                    Log.d(TAG, "尝试重连 WebSocket");
                    connect();
                }
            }
        };
        mainHandler.postDelayed(reconnectTask, config.reconnectInterval);
    }

    /**
     * 取消重连
     */
    private void cancelReconnect() {
        if (reconnectTask != null) {
            mainHandler.removeCallbacks(reconnectTask);
            reconnectTask = null;
        }
    }

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

        if (callback != null) {
            if (connected) {
                callback.onConnected();
            } else {
                callback.onDisconnected("连接断开");
            }
        }
    }

    /**
     * 保存连接状态到本地
     */
    private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
    }

    /**
     * 从本地恢复连接状态
     */
    private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
            config.url = savedUrl;
        }
    }

    /**
     * 内部 WebSocket 监听(OkHttp 的)
     */
    private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "WebSocket 连接成功");
            updateConnectionState(true);
            startHeartbeat();
            cancelReconnect();

            // 连接成功后发送缓存的消息
            sendCachedMessages();
        }

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

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

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "WebSocket 关闭: " + reason);
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "WebSocket 连接失败: " + t.getMessage());
            if (callback != null) {
                callback.onError(t.getMessage());
            }
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }
    }

    /**
     * 网络状态变化广播接收器
     */
    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                // 网络状态变化
                if (NetworkUtils.isNetworkAvailable(appContext)) {
                    Log.d(TAG, "网络已恢复,尝试连接WebSocket");
                    if (!isConnected && config.autoReconnect) {
                        connect();
                    }
                } else {
                    Log.d(TAG, "网络已断开");
                    if (isConnected) {
                        disconnect();
                    }
                }
            }
        }
    }
}

🔹 断线缓存功能说明

  1. 消息缓存

    • 新增两个线程安全队列:
      • messageQueue → 缓存文本消息
      • byteMessageQueue → 缓存二进制消息
    • sendMessage() 时,如果 WebSocket 未连接,则自动将消息加入对应队列
  2. 自动发送缓存消息

    • onOpen() 回调中调用 sendCachedMessages()
    • 遍历队列,依次发送所有缓存的消息
  3. 队列清空

    • 消息发送后会从队列中移除(poll() 方法)

🔹 使用方式不变

java

运行

复制代码
WebSocketConfig config = new WebSocketConfig();
config.url = "ws://your.socket.url";
config.heartbeatInterval = 10_000;
config.reconnectInterval = 5_000;
config.autoReconnect = true;
config.needHeartbeat = true;

WebSocketManager.getInstance().init(
        getApplicationContext(),
        config,
        new WebSocketCallback() {
            @Override
            public void onConnected() {
                Log.d("WS", "连接成功");
            }

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

            @Override
            public void onMessage(String message) {
                Log.d("WS", "收到文本消息: " + message);
            }

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

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

// 第一次连接
WebSocketManager.getInstance().connect();

// 即使在未连接状态下发送消息,也会自动缓存
WebSocketManager.getInstance().sendMessage("Hello WebSocket");

🔹 现在模块支持的功能

自动重连 (连接失败 / 断开时)✅ 心跳检测 (保持连接)✅ 连接状态持久化 (App 重启自动恢复)✅ 网络状态监听 (网络恢复自动重连)✅ 断线消息缓存与自动发送兼容 Android 16+

封装4

把消息缓存持久化到本地(SP 或数据库)

当前 WebSocket 封装的基础上,把消息缓存持久化到本地 ,这样即使 App 被杀死,重启后也能恢复未发送的消息,继续发送。我会用 SharedPreferences 来持久化(简单、无需额外依赖),并且保证兼容 Android 16+


🔹 修改 WebSocketManager.java

我会:

  1. 增加两个方法:saveMessageQueue()loadMessageQueue()

  2. 每次消息入队 / 出队时更新持久化缓存

  3. 在初始化时加载缓存的消息队列

    package com.example.socket;

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.content.SharedPreferences;
    import android.net.ConnectivityManager;
    import android.os.Handler;
    import android.os.Looper;
    import android.util.Log;

    import androidx.annotation.NonNull;

    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;

    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Queue;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.TimeUnit;

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

    /**

    • WebSocket 管理类(单例模式)

    • 功能:

      1. 自动重连
      1. 心跳检测
      1. 连接状态持久化
      1. 网络状态监听自动重连
      1. 断线消息缓存与自动发送
      1. 消息缓存持久化到本地(SP)
        */
        public class WebSocketManager {
        private static final String TAG = "WebSocketManager";
        private static final String PREF_NAME = "websocket_prefs";
        private static final String KEY_CONNECTED = "is_connected";
        private static final String KEY_URL = "ws_url";
        private static final String KEY_MESSAGE_QUEUE = "message_queue";
        private static final String KEY_BYTEMESSAGE_QUEUE = "byte_message_queue";

      private static volatile WebSocketManager instance;

      private OkHttpClient client;
      private WebSocket webSocket;
      private WebSocketConfig config;
      private WebSocketCallback callback;
      private boolean isConnected;
      private Handler mainHandler;
      private Runnable heartbeatTask;
      private Runnable reconnectTask;

      private Context appContext;
      private NetworkChangeReceiver networkReceiver;

      // 消息缓存队列(线程安全)
      private Queue<String> messageQueue = new ConcurrentLinkedQueue<>();
      private Queue<ByteString> byteMessageQueue = new ConcurrentLinkedQueue<>();

      private Gson gson = new Gson();

      private WebSocketManager() {
      mainHandler = new Handler(Looper.getMainLooper());
      client = new OkHttpClient.Builder()
      .readTimeout(0, TimeUnit.MILLISECONDS)
      .build();
      }

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

      /**

      • 初始化 WebSocket
        */
        public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        restoreConnectionState();
        loadMessageQueue(); // 加载持久化的消息队列

        // 注册网络监听
        registerNetworkListener();

        if (isConnected && config.url != null) {
        connect();
        }
        }

      /**

      • 注册网络状态监听广播
        */
        private void registerNetworkListener() {
        if (networkReceiver == null) {
        networkReceiver = new NetworkChangeReceiver();
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        appContext.registerReceiver(networkReceiver, filter);
        }
        }

      /**

      • 取消网络监听
        */
        public void unregisterNetworkListener() {
        if (networkReceiver != null) {
        try {
        appContext.unregisterReceiver(networkReceiver);
        } catch (Exception e) {
        e.printStackTrace();
        }
        networkReceiver = null;
        }
        }

      /**

      • 连接 WebSocket
        */
        public void connect() {
        if (isConnected || config == null || config.url == null) return;

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
        Log.e(TAG, "网络不可用,无法连接WebSocket");
        if (callback != null) callback.onError("网络不可用");
        scheduleReconnect();
        return;
        }

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        Log.d(TAG, "正在连接 WebSocket: " + config.url);
        }

      /**

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

      /**

      • 发送文本消息(断线时会缓存并持久化)
        */
        public void sendMessage(String text) {
        if (isConnected && webSocket != null) {
        webSocket.send(text);
        } else {
        Log.e(TAG, "WebSocket 未连接,缓存消息: " + text);
        messageQueue.offer(text); // 加入缓存队列
        saveMessageQueue(); // 持久化
        }
        }

      /**

      • 发送二进制消息(断线时会缓存并持久化)
        */
        public void sendMessage(ByteString bytes) {
        if (isConnected && webSocket != null) {
        webSocket.send(bytes);
        } else {
        Log.e(TAG, "WebSocket 未连接,缓存二进制消息");
        byteMessageQueue.offer(bytes); // 加入缓存队列
        saveMessageQueue(); // 持久化
        }
        }

      /**

      • 发送缓存的消息
        */
        private void sendCachedMessages() {
        // 发送文本消息
        while (!messageQueue.isEmpty()) {
        String msg = messageQueue.poll();
        if (msg != null) {
        webSocket.send(msg);
        Log.d(TAG, "发送缓存文本消息: " + msg);
        }
        }

        // 发送二进制消息
        while (!byteMessageQueue.isEmpty()) {
        ByteString bytes = byteMessageQueue.poll();
        if (bytes != null) {
        webSocket.send(bytes);
        Log.d(TAG, "发送缓存二进制消息");
        }
        }

        saveMessageQueue(); // 发送完毕后更新持久化
        }

      /**

      • 保存消息队列到本地
        */
        private void saveMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        // 保存文本消息队列
        List<String> msgList = new ArrayList<>(messageQueue);
        editor.putString(KEY_MESSAGE_QUEUE, gson.toJson(msgList));

        // 保存二进制消息队列(转为hex字符串)
        List<String> byteList = new ArrayList<>();
        for (ByteString bytes : byteMessageQueue) {
        byteList.add(bytes.hex());
        }
        editor.putString(KEY_BYTEMESSAGE_QUEUE, gson.toJson(byteList));

        editor.apply();
        }

      /**

      • 从本地加载消息队列
        */
        private void loadMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);

        // 加载文本消息队列
        String msgJson = sp.getString(KEY_MESSAGE_QUEUE, null);
        if (msgJson != null) {
        Type type = new TypeToken<List<String>>() {}.getType();
        List<String> msgList = gson.fromJson(msgJson, type);
        if (msgList != null) {
        messageQueue.addAll(msgList);
        }
        }

        // 加载二进制消息队列
        String byteJson = sp.getString(KEY_BYTEMESSAGE_QUEUE, null);
        if (byteJson != null) {
        Type type = new TypeToken<List<String>>() {}.getType();
        List<String> byteList = gson.fromJson(byteJson, type);
        if (byteList != null) {
        for (String hex : byteList) {
        byteMessageQueue.add(ByteString.decodeHex(hex));
        }
        }
        }

        Log.d(TAG, "从本地加载缓存消息: 文本消息 " + messageQueue.size() + " 条, 二进制消息 " + byteMessageQueue.size() + " 条");
        }

      /**

      • 启动心跳检测
        */
        private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        heartbeatTask = new Runnable() {
        @Override
        public void run() {
        if (isConnected && webSocket != null) {
        webSocket.send(ByteString.EMPTY); // 发送空消息作为心跳
        Log.d(TAG, "发送心跳包");
        }
        mainHandler.postDelayed(this, config.heartbeatInterval);
        }
        };
        mainHandler.postDelayed(heartbeatTask, config.heartbeatInterval);
        }

      /**

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

      /**

      • 安排重连
        */
        private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        cancelReconnect();
        reconnectTask = new Runnable() {
        @Override
        public void run() {
        if (!isConnected) {
        Log.d(TAG, "尝试重连 WebSocket");
        connect();
        }
        }
        };
        mainHandler.postDelayed(reconnectTask, config.reconnectInterval);
        }

      /**

      • 取消重连
        */
        private void cancelReconnect() {
        if (reconnectTask != null) {
        mainHandler.removeCallbacks(reconnectTask);
        reconnectTask = null;
        }
        }

      /**

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

        if (callback != null) {
        if (connected) {
        callback.onConnected();
        } else {
        callback.onDisconnected("连接断开");
        }
        }
        }

      /**

      • 保存连接状态到本地
        */
        private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
        }

      /**

      • 从本地恢复连接状态
        */
        private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
        config.url = savedUrl;
        }
        }

      /**

      • 内部 WebSocket 监听(OkHttp 的)
        */
        private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
        super.onOpen(webSocket, response);
        Log.d(TAG, "WebSocket 连接成功");
        updateConnectionState(true);
        startHeartbeat();
        cancelReconnect();

        复制代码
         // 连接成功后发送缓存的消息
         sendCachedMessages();

        }

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

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

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
        super.onClosed(webSocket, code, reason);
        Log.d(TAG, "WebSocket 关闭: " + reason);
        updateConnectionState(false);
        stopHeartbeat();
        scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        Log.e(TAG, "WebSocket 连接失败: " + t.getMessage());
        if (callback != null) {
        callback.onError(t.getMessage());
        }
        updateConnectionState(false);
        stopHeartbeat();
        scheduleReconnect();
        }
        }

      /**

      • 网络状态变化广播接收器
        */
        private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
        // 网络状态变化
        if (NetworkUtils.isNetworkAvailable(appContext)) {
        Log.d(TAG, "网络已恢复,尝试连接WebSocket");
        if (!isConnected && config.autoReconnect) {
        connect();
        }
        } else {
        Log.d(TAG, "网络已断开");
        if (isConnected) {
        disconnect();
        }
        }
        }
        }
        }
        }

🔹 持久化缓存功能说明

  1. 存储方式

    • 使用 SharedPreferences 存储
    • 文本消息 → 直接存字符串列表
    • 二进制消息 → 转为 Hex 字符串再存
  2. Gson 依赖

    • 需要在 build.gradle 中添加 Gson 依赖:

      gradle

      复制代码
      implementation 'com.google.code.gson:gson:2.8.9'
  3. 持久化时机

    • 每次有新消息加入缓存队列时
    • 每次发送完缓存消息后
    • 初始化时加载
  4. 恢复机制

    • App 重启后,init() 时会调用 loadMessageQueue() 恢复消息队列
    • 连接成功后自动发送这些消息

🔹 现在模块支持的功能

自动重连心跳检测连接状态持久化网络状态监听自动重连断线消息缓存与自动发送消息缓存持久化到本地(SP)兼容 Android 16+

封装5

再加上缓存消息数量限制 ,避免 SP 存储过多数据导致性能问题。加上消息数量限制和清理策略

在现有的 WebSocket 封装模块上增加消息数量限制和清理策略,这样可以避免 SP 存储过多数据导致性能下降或占用过多内存。

我会:

  • 给文本消息和二进制消息队列分别设置最大数量限制
  • 当队列满时,采用 FIFO(先进先出)策略移除最早的消息
  • 提供手动清理缓存的方法

🔹 修改 WebSocketConfig.java

增加两个配置项:

复制代码
package com.example.socket;

/**
 * WebSocket 配置类
 */
public class WebSocketConfig {
    public String url;                 // WebSocket 连接地址
    public long heartbeatInterval = 10_000; // 心跳间隔(毫秒)
    public long reconnectInterval = 5_000;  // 重连间隔(毫秒)
    public boolean autoReconnect = true;   // 是否自动重连
    public boolean needHeartbeat = true;   // 是否需要心跳
    public int maxTextMessageCacheSize = 100; // 最大文本消息缓存数量
    public int maxBinaryMessageCacheSize = 50; // 最大二进制消息缓存数量
}

package com.nyw.mvvmmode.net.websocket;



import okio.ByteString;

/**
 * WebSocket 回调接口
 * 所有方法运行在主线程
 */
public interface WebSocketCallback {

    /**
     * 连接成功回调
     */
    void onConnected();

    /**
     * 连接断开回调
     * @param reason 断开原因
     */
    void onDisconnected(String reason);

    /**
     * 收到文本消息
     * @param message 消息内容
     */
    void onMessage(String message);

    /**
     * 收到二进制消息
     * @param bytes 二进制数据
     */
    void onMessage(ByteString bytes);

    /**
     * 发生错误
     * @param error 错误信息
     */
    void onError(String error);
}

3️⃣ NetworkUtils.java(网络工具类)

复制代码
package com.example.socket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/**
 * 网络工具类(兼容 Android 16+)
 */
public class NetworkUtils {

    /**
     * 判断网络是否可用
     */
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager cm = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        if (cm == null) return false;

        // 低版本使用 getAllNetworkInfo()
        NetworkInfo[] info = cm.getAllNetworkInfo();
        if (info != null) {
            for (NetworkInfo networkInfo : info) {
                if (networkInfo.getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
        return false;
    }
}

4️⃣ WebSocketManager.java(核心管理类)

复制代码
package com.example.socket;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

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

/**
 * WebSocket 管理类(单例模式)
 * 功能:
 * 1. 自动重连
 * 2. 心跳检测
 * 3. 连接状态持久化
 * 4. 网络状态监听自动重连
 * 5. 断线消息缓存与自动发送
 * 6. 消息缓存持久化到本地(SP)
 * 7. 消息数量限制和清理策略
 * 8. 兼容 Android 16+
 */
public class WebSocketManager {
    private static final String TAG = "WebSocketManager";
    private static final String PREF_NAME = "websocket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_URL = "ws_url";
    private static final String KEY_MESSAGE_QUEUE = "message_queue";
    private static final String KEY_BYTEMESSAGE_QUEUE = "byte_message_queue";

    // 默认最大缓存数量
    private static final int DEFAULT_MAX_TEXT_CACHE = 100;
    private static final int DEFAULT_MAX_BINARY_CACHE = 50;

    private static volatile WebSocketManager instance;

    private OkHttpClient client;
    private WebSocket webSocket;
    private WebSocketConfig config;
    private WebSocketCallback callback;
    private boolean isConnected;
    private Handler mainHandler;
    private Runnable heartbeatTask;
    private Runnable reconnectTask;

    private Context appContext;
    private NetworkChangeReceiver networkReceiver;

    // 消息缓存队列(线程安全)
    private Queue<String> messageQueue = new ConcurrentLinkedQueue<>();
    private Queue<ByteString> byteMessageQueue = new ConcurrentLinkedQueue<>();

    private Gson gson = new Gson();

    private WebSocketManager() {
        mainHandler = new Handler(Looper.getMainLooper());
        client = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();
    }

    /**
     * 获取单例实例
     */
    public static WebSocketManager getInstance() {
        if (instance == null) {
            synchronized (WebSocketManager.class) {
                if (instance == null) {
                    instance = new WebSocketManager();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化 WebSocket
     */
    public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        // 设置默认缓存大小
        if (config.maxTextMessageCacheSize <= 0) {
            config.maxTextMessageCacheSize = DEFAULT_MAX_TEXT_CACHE;
        }
        if (config.maxBinaryMessageCacheSize <= 0) {
            config.maxBinaryMessageCacheSize = DEFAULT_MAX_BINARY_CACHE;
        }

        restoreConnectionState();
        loadMessageQueue(); // 加载持久化的消息队列
        trimMessageQueue(); // 确保不超过最大限制

        // 注册网络监听
        registerNetworkListener();

        if (isConnected && config.url != null) {
            connect();
        }
    }

    /**
     * 注册网络状态监听广播
     */
    private void registerNetworkListener() {
        if (networkReceiver == null) {
            networkReceiver = new NetworkChangeReceiver();
            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
            appContext.registerReceiver(networkReceiver, filter);
        }
    }

    /**
     * 取消网络监听
     */
    public void unregisterNetworkListener() {
        if (networkReceiver != null) {
            try {
                appContext.unregisterReceiver(networkReceiver);
            } catch (Exception e) {
                e.printStackTrace();
            }
            networkReceiver = null;
        }
    }

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

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
            Log.e(TAG, "网络不可用,无法连接WebSocket");
            if (callback != null) callback.onError("网络不可用");
            scheduleReconnect();
            return;
        }

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        Log.d(TAG, "正在连接 WebSocket: " + config.url);
    }

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

    /**
     * 发送文本消息(断线时会缓存并持久化)
     */
    public void sendMessage(String text) {
        if (isConnected && webSocket != null) {
            webSocket.send(text);
        } else {
            Log.e(TAG, "WebSocket 未连接,缓存消息: " + text);
            messageQueue.offer(text); // 加入缓存队列
            trimTextMessageQueue();  // 检查并裁剪队列
            saveMessageQueue(); // 持久化
        }
    }

    /**
     * 发送二进制消息(断线时会缓存并持久化)
     */
    public void sendMessage(ByteString bytes) {
        if (isConnected && webSocket != null) {
            webSocket.send(bytes);
        } else {
            Log.e(TAG, "WebSocket 未连接,缓存二进制消息");
            byteMessageQueue.offer(bytes); // 加入缓存队列
            trimBinaryMessageQueue(); // 检查并裁剪队列
            saveMessageQueue(); // 持久化
        }
    }

    /**
     * 发送缓存的消息
     */
    private void sendCachedMessages() {
        // 发送文本消息
        while (!messageQueue.isEmpty()) {
            String msg = messageQueue.poll();
            if (msg != null) {
                webSocket.send(msg);
                Log.d(TAG, "发送缓存文本消息: " + msg);
            }
        }

        // 发送二进制消息
        while (!byteMessageQueue.isEmpty()) {
            ByteString bytes = byteMessageQueue.poll();
            if (bytes != null) {
                webSocket.send(bytes);
                Log.d(TAG, "发送缓存二进制消息");
            }
        }

        saveMessageQueue(); // 发送完毕后更新持久化
    }

    /**
     * 保存消息队列到本地
     */
    private void saveMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        // 保存文本消息队列
        List<String> msgList = new ArrayList<>(messageQueue);
        editor.putString(KEY_MESSAGE_QUEUE, gson.toJson(msgList));

        // 保存二进制消息队列(转为hex字符串)
        List<String> byteList = new ArrayList<>();
        for (ByteString bytes : byteMessageQueue) {
            byteList.add(bytes.hex());
        }
        editor.putString(KEY_BYTEMESSAGE_QUEUE, gson.toJson(byteList));

        editor.apply();
    }

    /**
     * 从本地加载消息队列
     */
    private void loadMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);

        // 加载文本消息队列
        String msgJson = sp.getString(KEY_MESSAGE_QUEUE, null);
        if (msgJson != null) {
            Type type = new TypeToken<List<String>>() {}.getType();
            List<String> msgList = gson.fromJson(msgJson, type);
            if (msgList != null) {
                messageQueue.addAll(msgList);
            }
        }

        // 加载二进制消息队列
        String byteJson = sp.getString(KEY_BYTEMESSAGE_QUEUE, null);
        if (byteJson != null) {
            Type type = new TypeToken<List<String>>() {}.getType();
            List<String> byteList = gson.fromJson(byteJson, type);
            if (byteList != null) {
                for (String hex : byteList) {
                    byteMessageQueue.add(ByteString.decodeHex(hex));
                }
            }
        }

        Log.d(TAG, "从本地加载缓存消息: 文本消息 " + messageQueue.size() + " 条, 二进制消息 " + byteMessageQueue.size() + " 条");
    }

    /**
     * 裁剪文本消息队列
     */
    private void trimTextMessageQueue() {
        int maxSize = config.maxTextMessageCacheSize;
        while (messageQueue.size() > maxSize) {
            String removed = messageQueue.poll();
            Log.d(TAG, "文本消息缓存超过上限,移除最早的消息: " + removed);
        }
    }

    /**
     * 裁剪二进制消息队列
     */
    private void trimBinaryMessageQueue() {
        int maxSize = config.maxBinaryMessageCacheSize;
        while (byteMessageQueue.size() > maxSize) {
            ByteString removed = byteMessageQueue.poll();
            Log.d(TAG, "二进制消息缓存超过上限,移除最早的消息");
        }
    }

    /**
     * 裁剪所有消息队列
     */
    private void trimMessageQueue() {
        trimTextMessageQueue();
        trimBinaryMessageQueue();
    }

    /**
     * 手动清理所有缓存消息
     */
    public void clearCachedMessages() {
        messageQueue.clear();
        byteMessageQueue.clear();
        saveMessageQueue();
        Log.d(TAG, "手动清理所有缓存消息");
    }

    /**
     * 手动清理文本消息缓存
     */
    public void clearTextMessageCache() {
        messageQueue.clear();
        saveMessageQueue();
        Log.d(TAG, "手动清理文本消息缓存");
    }

    /**
     * 手动清理二进制消息缓存
     */
    public void clearBinaryMessageCache() {
        byteMessageQueue.clear();
        saveMessageQueue();
        Log.d(TAG, "手动清理二进制消息缓存");
    }

    /**
     * 启动心跳检测
     */
    private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        heartbeatTask = new Runnable() {
            @Override
            public void run() {
                if (isConnected && webSocket != null) {
                    webSocket.send(ByteString.EMPTY); // 发送空消息作为心跳
                    Log.d(TAG, "发送心跳包");
                }
                mainHandler.postDelayed(this, config.heartbeatInterval);
            }
        };
        mainHandler.postDelayed(heartbeatTask, config.heartbeatInterval);
    }

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

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        cancelReconnect();
        reconnectTask = new Runnable() {
            @Override
            public void run() {
                if (!isConnected) {
                    Log.d(TAG, "尝试重连 WebSocket");
                    connect();
                }
            }
        };
        mainHandler.postDelayed(reconnectTask, config.reconnectInterval);
    }

    /**
     * 取消重连
     */
    private void cancelReconnect() {
        if (reconnectTask != null) {
            mainHandler.removeCallbacks(reconnectTask);
            reconnectTask = null;
        }
    }

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

        if (callback != null) {
            if (connected) {
                callback.onConnected();
            } else {
                callback.onDisconnected("连接断开");
            }
        }
    }

    /**
     * 保存连接状态到本地
     */
    private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
    }

    /**
     * 从本地恢复连接状态
     */
    private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
            config.url = savedUrl;
        }
    }

    /**
     * 内部 WebSocket 监听(OkHttp 的)
     */
    private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "WebSocket 连接成功");
            updateConnectionState(true);
            startHeartbeat();
            cancelReconnect();

            // 连接成功后发送缓存的消息
            sendCachedMessages();
        }

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

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

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "WebSocket 关闭: " + reason);
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "WebSocket 连接失败: " + t.getMessage());
            if (callback != null) {
                callback.onError(t.getMessage());
            }
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }
    }

    /**
     * 网络状态变化广播接收器
     */
    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                // 网络状态变化
                if (NetworkUtils.isNetworkAvailable(appContext)) {
                    Log.d(TAG, "网络已恢复,尝试连接WebSocket");
                    if (!isConnected && config.autoReconnect) {
                        connect();
                    }
                } else {
                    Log.d(TAG, "网络已断开");
                    if (isConnected) {
                        disconnect();
                    }
                }
            }
        }
    }
}

🚀 使用示例

java

运行

复制代码
// 1. 创建配置
WebSocketConfig config = new WebSocketConfig();
config.url = "ws://your.socket.url";
config.heartbeatInterval = 10_000; // 10秒
config.reconnectInterval = 5_000;  // 5秒
config.autoReconnect = true;
config.needHeartbeat = true;
config.maxTextMessageCacheSize = 150; // 自定义文本消息缓存上限
config.maxBinaryMessageCacheSize = 80; // 自定义二进制消息缓存上限

// 2. 初始化并设置回调
WebSocketManager.getInstance().init(
        getApplicationContext(),
        config,
        new WebSocketCallback() {
            @Override
            public void onConnected() {
                Log.d("WS", "连接成功");
            }

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

            @Override
            public void onMessage(String message) {
                Log.d("WS", "收到文本消息: " + message);
            }

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

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

// 3. 连接
WebSocketManager.getInstance().connect();

// 4. 发送消息(即使未连接也会自动缓存)
WebSocketManager.getInstance().sendMessage("Hello WebSocket");

// 5. 手动清理缓存(需要时)
// WebSocketManager.getInstance().clearCachedMessages();

📋 功能总结

自动重连 (连接失败 / 断开时)✅ 心跳检测 (保持连接)✅ 连接状态持久化 (App 重启自动恢复)✅ 网络状态监听 (网络恢复自动重连)✅ 断线消息缓存与自动发送消息缓存持久化到本地(SP)消息数量限制和清理策略 (FIFO)✅ 手动清理缓存方法兼容 Android 16+

封装6

加上消息时间限制,比如只保留最近 24 小时的消息,这样可以更智能地管理缓存。

  • 在缓存消息时记录时间戳
  • 提供时间限制配置(默认 24 小时)
  • 在加载和添加消息时清理过期消息
  • 兼容 Android 16+

🔹 修改 WebSocketConfig.java

增加消息时间限制配置:

复制代码
package com.example.socket;

/**
 * WebSocket 配置类
 */
public class WebSocketConfig {
    public String url;                 // WebSocket 连接地址
    public long heartbeatInterval = 10_000; // 心跳间隔(毫秒)
    public long reconnectInterval = 5_000;  // 重连间隔(毫秒)
    public boolean autoReconnect = true;   // 是否自动重连
    public boolean needHeartbeat = true;   // 是否需要心跳
    public int maxTextMessageCacheSize = 100; // 最大文本消息缓存数量
    public int maxBinaryMessageCacheSize = 50; // 最大二进制消息缓存数量
    public long messageExpiryTime = 24 * 60 * 60 * 1000; // 消息过期时间(毫秒),默认24小时
}

🔹 创建消息包装类

创建一个类来包装消息和时间戳:

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


import okio.ByteString;

/**
 * 带时间戳的消息包装类
 * 用于实现消息时间限制功能
 */
public class TimedMessage {
    public String text;          // 文本消息内容
    public String byteHex;       // 二进制消息的Hex字符串表示
    public long timestamp;       // 消息创建时间戳

    /**
     * 构造文本消息
     */
    public TimedMessage(String text) {
        this.text = text;
        this.timestamp = System.currentTimeMillis();
    }

    /**
     * 构造二进制消息
     */
    public TimedMessage(ByteString bytes) {
        this.byteHex = bytes.hex();
        this.timestamp = System.currentTimeMillis();
    }

    /**
     * 判断消息是否过期
     * @param expiryTime 过期时间(毫秒)
     * @return 是否过期
     */
    public boolean isExpired(long expiryTime) {
        return System.currentTimeMillis() - timestamp > expiryTime;
    }

    /**
     * 获取二进制消息对象
     * @return ByteString对象,如果不是二进制消息则返回null
     */
    public ByteString getByteString() {
        if (byteHex != null) {
            return ByteString.decodeHex(byteHex);
        }
        return null;
    }
}

package com.example.socket;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

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

/**
 * WebSocket 管理类(单例模式)
 * 功能:
 * 1. 自动重连
 * 2. 心跳检测
 * 3. 连接状态持久化
 * 4. 网络状态监听自动重连
 * 5. 断线消息缓存与自动发送
 * 6. 消息缓存持久化到本地(SP)
 * 7. 消息数量限制和清理策略
 * 8. 消息时间限制(自动清理过期消息)
 * 9. 兼容 Android 16+
 */
public class WebSocketManager {
    private static final String TAG = "WebSocketManager";
    private static final String PREF_NAME = "websocket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_URL = "ws_url";
    private static final String KEY_MESSAGE_QUEUE = "message_queue";
    private static final String KEY_BYTEMESSAGE_QUEUE = "byte_message_queue";

    // 默认最大缓存数量
    private static final int DEFAULT_MAX_TEXT_CACHE = 100;
    private static final int DEFAULT_MAX_BINARY_CACHE = 50;

    private static volatile WebSocketManager instance;

    private OkHttpClient client;
    private WebSocket webSocket;
    private WebSocketConfig config;
    private WebSocketCallback callback;
    private boolean isConnected;
    private Handler mainHandler;
    private Runnable heartbeatTask;
    private Runnable reconnectTask;

    private Context appContext;
    private NetworkChangeReceiver networkReceiver;

    // 消息缓存队列(线程安全),使用TimedMessage包装以支持时间限制
    private Queue<TimedMessage> messageQueue = new ConcurrentLinkedQueue<>();
    private Queue<TimedMessage> byteMessageQueue = new ConcurrentLinkedQueue<>();

    private Gson gson = new Gson();

    private WebSocketManager() {
        mainHandler = new Handler(Looper.getMainLooper());
        client = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();
    }

    /**
     * 获取单例实例
     */
    public static WebSocketManager getInstance() {
        if (instance == null) {
            synchronized (WebSocketManager.class) {
                if (instance == null) {
                    instance = new WebSocketManager();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化 WebSocket
     */
    public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        // 设置默认缓存大小
        if (config.maxTextMessageCacheSize <= 0) {
            config.maxTextMessageCacheSize = DEFAULT_MAX_TEXT_CACHE;
        }
        if (config.maxBinaryMessageCacheSize <= 0) {
            config.maxBinaryMessageCacheSize = DEFAULT_MAX_BINARY_CACHE;
        }

        restoreConnectionState();
        loadMessageQueue(); // 加载持久化的消息队列
        trimMessageQueue(); // 确保不超过最大限制和时间限制

        // 注册网络监听
        registerNetworkListener();

        if (isConnected && config.url != null) {
            connect();
        }
    }

    /**
     * 注册网络状态监听广播
     */
    private void registerNetworkListener() {
        if (networkReceiver == null) {
            networkReceiver = new NetworkChangeReceiver();
            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
            appContext.registerReceiver(networkReceiver, filter);
        }
    }

    /**
     * 取消网络监听
     */
    public void unregisterNetworkListener() {
        if (networkReceiver != null) {
            try {
                appContext.unregisterReceiver(networkReceiver);
            } catch (Exception e) {
                e.printStackTrace();
            }
            networkReceiver = null;
        }
    }

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

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
            Log.e(TAG, "网络不可用,无法连接WebSocket");
            if (callback != null) callback.onError("网络不可用");
            scheduleReconnect();
            return;
        }

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        Log.d(TAG, "正在连接 WebSocket: " + config.url);
    }

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

    /**
     * 发送文本消息(断线时会缓存并持久化)
     */
    public void sendMessage(String text) {
        if (isConnected && webSocket != null) {
            webSocket.send(text);
        } else {
            Log.e(TAG, "WebSocket 未连接,缓存消息: " + text);
            messageQueue.offer(new TimedMessage(text)); // 加入缓存队列
            trimTextMessageQueue();  // 检查并裁剪队列
            saveMessageQueue(); // 持久化
        }
    }

    /**
     * 发送二进制消息(断线时会缓存并持久化)
     */
    public void sendMessage(ByteString bytes) {
        if (isConnected && webSocket != null) {
            webSocket.send(bytes);
        } else {
            Log.e(TAG, "WebSocket 未连接,缓存二进制消息");
            byteMessageQueue.offer(new TimedMessage(bytes)); // 加入缓存队列
            trimBinaryMessageQueue(); // 检查并裁剪队列
            saveMessageQueue(); // 持久化
        }
    }

    /**
     * 发送缓存的消息
     */
    private void sendCachedMessages() {
        // 发送文本消息
        while (!messageQueue.isEmpty()) {
            TimedMessage msg = messageQueue.poll();
            if (msg != null && msg.text != null) {
                webSocket.send(msg.text);
                Log.d(TAG, "发送缓存文本消息: " + msg.text);
            }
        }

        // 发送二进制消息
        while (!byteMessageQueue.isEmpty()) {
            TimedMessage msg = byteMessageQueue.poll();
            if (msg != null && msg.getByteString() != null) {
                webSocket.send(msg.getByteString());
                Log.d(TAG, "发送缓存二进制消息");
            }
        }

        saveMessageQueue(); // 发送完毕后更新持久化
    }

    /**
     * 保存消息队列到本地
     */
    private void saveMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        // 保存文本消息队列
        List<TimedMessage> msgList = new ArrayList<>(messageQueue);
        editor.putString(KEY_MESSAGE_QUEUE, gson.toJson(msgList));

        // 保存二进制消息队列
        List<TimedMessage> byteList = new ArrayList<>(byteMessageQueue);
        editor.putString(KEY_BYTEMESSAGE_QUEUE, gson.toJson(byteList));

        editor.apply();
    }

    /**
     * 从本地加载消息队列
     */
    private void loadMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);

        // 加载文本消息队列
        String msgJson = sp.getString(KEY_MESSAGE_QUEUE, null);
        if (msgJson != null) {
            Type type = new TypeToken<List<TimedMessage>>() {}.getType();
            List<TimedMessage> msgList = gson.fromJson(msgJson, type);
            if (msgList != null) {
                messageQueue.addAll(msgList);
            }
        }

        // 加载二进制消息队列
        String byteJson = sp.getString(KEY_BYTEMESSAGE_QUEUE, null);
        if (byteJson != null) {
            Type type = new TypeToken<List<TimedMessage>>() {}.getType();
            List<TimedMessage> byteList = gson.fromJson(byteJson, type);
            if (byteList != null) {
                byteMessageQueue.addAll(byteList);
            }
        }

        Log.d(TAG, "从本地加载缓存消息: 文本消息 " + messageQueue.size() + " 条, 二进制消息 " + byteMessageQueue.size() + " 条");
    }

    /**
     * 裁剪文本消息队列(按数量和时间)
     */
    private void trimTextMessageQueue() {
        // 移除过期消息
        long expiryTime = config.messageExpiryTime;
        while (!messageQueue.isEmpty() && messageQueue.peek().isExpired(expiryTime)) {
            TimedMessage removed = messageQueue.poll();
            Log.d(TAG, "文本消息过期,移除: " + removed.text);
        }

        // 移除超出数量限制的消息
        int maxSize = config.maxTextMessageCacheSize;
        while (messageQueue.size() > maxSize) {
            TimedMessage removed = messageQueue.poll();
            Log.d(TAG, "文本消息缓存超过上限,移除最早的消息: " + removed.text);
        }
    }

    /**
     * 裁剪二进制消息队列(按数量和时间)
     */
    private void trimBinaryMessageQueue() {
        // 移除过期消息
        long expiryTime = config.messageExpiryTime;
        while (!byteMessageQueue.isEmpty() && byteMessageQueue.peek().isExpired(expiryTime)) {
            byteMessageQueue.poll();
            Log.d(TAG, "二进制消息过期,移除");
        }

        // 移除超出数量限制的消息
        int maxSize = config.maxBinaryMessageCacheSize;
        while (byteMessageQueue.size() > maxSize) {
            byteMessageQueue.poll();
            Log.d(TAG, "二进制消息缓存超过上限,移除最早的消息");
        }
    }

    /**
     * 裁剪所有消息队列
     */
    private void trimMessageQueue() {
        trimTextMessageQueue();
        trimBinaryMessageQueue();
    }

    /**
     * 手动清理所有缓存消息
     */
    public void clearCachedMessages() {
        messageQueue.clear();
        byteMessageQueue.clear();
        saveMessageQueue();
        Log.d(TAG, "手动清理所有缓存消息");
    }

    /**
     * 手动清理文本消息缓存
     */
    public void clearTextMessageCache() {
        messageQueue.clear();
        saveMessageQueue();
        Log.d(TAG, "手动清理文本消息缓存");
    }

    /**
     * 手动清理二进制消息缓存
     */
    public void clearBinaryMessageCache() {
        byteMessageQueue.clear();
        saveMessageQueue();
        Log.d(TAG, "手动清理二进制消息缓存");
    }

    /**
     * 启动心跳检测
     */
    private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        heartbeatTask = new Runnable() {
            @Override
            public void run() {
                if (isConnected && webSocket != null) {
                    webSocket.send(ByteString.EMPTY); // 发送空消息作为心跳
                    Log.d(TAG, "发送心跳包");
                }
                mainHandler.postDelayed(this, config.heartbeatInterval);
            }
        };
        mainHandler.postDelayed(heartbeatTask, config.heartbeatInterval);
    }

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

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        cancelReconnect();
        reconnectTask = new Runnable() {
            @Override
            public void run() {
                if (!isConnected) {
                    Log.d(TAG, "尝试重连 WebSocket");
                    connect();
                }
            }
        };
        mainHandler.postDelayed(reconnectTask, config.reconnectInterval);
    }

    /**
     * 取消重连
     */
    private void cancelReconnect() {
        if (reconnectTask != null) {
            mainHandler.removeCallbacks(reconnectTask);
            reconnectTask = null;
        }
    }

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

        if (callback != null) {
            if (connected) {
                callback.onConnected();
            } else {
                callback.onDisconnected("连接断开");
            }
        }
    }

    /**
     * 保存连接状态到本地
     */
    private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, isConnected);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
    }

    /**
     * 从本地恢复连接状态
     */
    private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        isConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
            config.url = savedUrl;
        }
    }

    /**
     * 内部 WebSocket 监听(OkHttp 的)
     */
    private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            Log.d(TAG, "WebSocket 连接成功");
            updateConnectionState(true);
            startHeartbeat();
            cancelReconnect();

            // 连接成功后发送缓存的消息
            sendCachedMessages();
        }

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

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

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            Log.d(TAG, "WebSocket 关闭: " + reason);
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            Log.e(TAG, "WebSocket 连接失败: " + t.getMessage());
            if (callback != null) {
                callback.onError(t.getMessage());
            }
            updateConnectionState(false);
            stopHeartbeat();
            scheduleReconnect();
        }
    }

    /**
     * 网络状态变化广播接收器
     */
    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                // 网络状态变化
                if (NetworkUtils.isNetworkAvailable(appContext)) {
                    Log.d(TAG, "网络已恢复,尝试连接WebSocket");
                    if (!isConnected && config.autoReconnect) {
                        connect();
                    }
                } else {
                    Log.d(TAG, "网络已断开");
                    if (isConnected) {
                        disconnect();
                    }
                }
            }
        }
    }
}

🚀 使用示例

java

运行

复制代码
// 1. 创建配置
WebSocketConfig config = new WebSocketConfig();
config.url = "ws://your.socket.url";
config.heartbeatInterval = 10_000; // 10秒
config.reconnectInterval = 5_000;  // 5秒
config.autoReconnect = true;
config.needHeartbeat = true;
config.maxTextMessageCacheSize = 150; // 自定义文本消息缓存上限
config.maxBinaryMessageCacheSize = 80; // 自定义二进制消息缓存上限
config.messageExpiryTime = 12 * 60 * 60 * 1000; // 设置消息过期时间为12小时

// 2. 初始化并设置回调
WebSocketManager.getInstance().init(
        getApplicationContext(),
        config,
        new WebSocketCallback() {
            @Override
            public void onConnected() {
                Log.d("WS", "连接成功");
            }

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

            @Override
            public void onMessage(String message) {
                Log.d("WS", "收到文本消息: " + message);
            }

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

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

// 3. 连接
WebSocketManager.getInstance().connect();

// 4. 发送消息(即使未连接也会自动缓存)
WebSocketManager.getInstance().sendMessage("Hello WebSocket");

// 5. 手动清理缓存(需要时)
// WebSocketManager.getInstance().clearCachedMessages();

📋 功能总结

自动重连 (连接失败 / 断开时)✅ 心跳检测 (保持连接)✅ 连接状态持久化 (App 重启自动恢复)✅ 网络状态监听 (网络恢复自动重连)✅ 断线消息缓存与自动发送消息缓存持久化到本地(SP)消息数量限制和清理策略 (FIFO)✅ 消息时间限制 (自动清理过期消息)✅ 手动清理缓存方法兼容 Android 16+

封装7

企业级 WebSocket 封装库,支持 MVVM 架构和独立使用,增加了所有你需要的功能:

  • 后台线程处理消息发送(避免 UI 阻塞)
  • 消息发送状态回调(成功 / 失败)
  • 消息优先级支持(重要消息优先)
  • 批量发送与节流控制
  • 消息发送超时处理
  • 断线重连策略优化
  • 连接状态 LiveData(MVVM 支持)
  • 完整的缓存管理(数量 + 时间限制)

📂 模块结构

plaintext

复制代码
com.example.socket/
├── config/              # 配置相关
│   ├── WebSocketConfig.java
│   ├── MessagePriority.java
│   └── ConnectionState.java
├── callback/            # 回调接口
│   ├── WebSocketCallback.java
│   └── SendCallback.java
├── model/               # 数据模型
│   ├── TimedMessage.java
│   ├── PriorityMessage.java
│   └── WebSocketEvent.java
├── util/                # 工具类
│   ├── NetworkUtils.java
│   └── ThreadUtils.java
├── manager/             # 核心管理类
│   ├── WebSocketManager.java
│   └── WebSocketLiveData.java
└── annotation/          # 注解
    └── Priority.java

1️⃣ 配置类

WebSocketConfig.java

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





/**
 * WebSocket配置类
 */
public class WebSocketConfig {
    public String url;                 // WebSocket连接地址
    public long heartbeatInterval = 10_000; // 心跳间隔(毫秒)
    public long reconnectInterval = 5_000;  // 重连间隔(毫秒)
    public int maxReconnectAttempts = 0;   // 最大重连次数,0表示无限
    public boolean autoReconnect = true;   // 是否自动重连
    public boolean needHeartbeat = true;   // 是否需要心跳
    public int maxTextMessageCacheSize = 100; // 最大文本消息缓存数量
    public int maxBinaryMessageCacheSize = 50; // 最大二进制消息缓存数量
    public long messageExpiryTime = 24 * 60 * 60 * 1000; // 消息过期时间(毫秒)
    public long sendTimeout = 10_000;  // 消息发送超时时间(毫秒)
    public int batchSendSize = 10;     // 批量发送消息数量
    public long batchSendDelay = 500;  // 批量发送延迟(毫秒)
    public boolean logEnabled = true;  // 是否启用日志
}

MessagePriority.java

java

运行

复制代码
package com.example.socket.config;

/**
 * 消息优先级枚举
 */
public enum MessagePriority {
    LOW,    // 低优先级
    NORMAL, // 普通优先级
    HIGH,   // 高优先级
    IMMEDIATE // 立即发送
}

ConnectionState.java

java

运行

复制代码
package com.example.socket.config;

/**
 * 连接状态枚举
 */
public enum ConnectionState {
    DISCONNECTED,  // 未连接
    CONNECTING,    // 连接中
    CONNECTED,     // 已连接
    RECONNECTING   // 重连中
}

2️⃣ 回调接口

WebSocketCallback.java

复制代码
package com.example.socket.callback;

import com.example.socket.config.ConnectionState;
import okio.ByteString;

/**
 * WebSocket回调接口
 */
public interface WebSocketCallback {
    /**
     * 连接状态变化
     */
    void onConnectionStateChanged(ConnectionState state);
    
    /**
     * 收到文本消息
     */
    void onMessage(String message);
    
    /**
     * 收到二进制消息
     */
    void onMessage(ByteString bytes);
    
    /**
     * 发生错误
     */
    void onError(String error);
}

SendCallback.java

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


/**
 * 消息发送回调
 */
public interface SendCallback {
    /**
     * 消息发送成功
     */
    void onSuccess();

    /**
     * 消息发送失败
     * @param error 失败原因
     */
    void onFailure(String error);
}

3️⃣ 数据模型

TimedMessage.java

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


import okio.ByteString;

/**
 * 带时间戳的消息
 */
public class TimedMessage {
    public String text;      // 文本消息
    public String byteHex;   // 二进制消息的Hex表示
    public long timestamp;   // 时间戳

    public TimedMessage(String text) {
        this.text = text;
        this.timestamp = System.currentTimeMillis();
    }

    public TimedMessage(ByteString bytes) {
        this.byteHex = bytes.hex();
        this.timestamp = System.currentTimeMillis();
    }

    /**
     * 判断消息是否过期
     */
    public boolean isExpired(long expiryTime) {
        return System.currentTimeMillis() - timestamp > expiryTime;
    }

    /**
     * 获取二进制消息
     */
    public ByteString getByteString() {
        if (byteHex != null) {
            return ByteString.decodeHex(byteHex);
        }
        return null;
    }
}

PriorityMessage.java

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

import okio.ByteString;

/**
 * 带优先级的消息
 */
public class PriorityMessage {
    public String text;
    public ByteString bytes;
    public MessagePriority priority;
    public SendCallback callback;
    public long timestamp;
    public String tag;  // 消息标签,用于追踪

    private PriorityMessage() {
        this.timestamp = System.currentTimeMillis();
    }

    public static PriorityMessage text(String text, MessagePriority priority, SendCallback callback) {
        PriorityMessage msg = new PriorityMessage();
        msg.text = text;
        msg.priority = priority;
        msg.callback = callback;
        return msg;
    }

    public static PriorityMessage binary(ByteString bytes, MessagePriority priority, SendCallback callback) {
        PriorityMessage msg = new PriorityMessage();
        msg.bytes = bytes;
        msg.priority = priority;
        msg.callback = callback;
        return msg;
    }

    public boolean isText() {
        return text != null;
    }

    public boolean isBinary() {
        return bytes != null;
    }
}

WebSocketEvent.java

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

import okio.ByteString;

/**
 * WebSocket事件类(用于LiveData)
 */
public class WebSocketEvent {
    public static final int TYPE_STATE_CHANGE = 1;
    public static final int TYPE_TEXT_MESSAGE = 2;
    public static final int TYPE_BINARY_MESSAGE = 3;
    public static final int TYPE_ERROR = 4;
    public static final int TYPE_SEND_SUCCESS = 5;
    public static final int TYPE_SEND_FAILURE = 6;

    public int type;
    public ConnectionState state;
    public String textMessage;
    public ByteString binaryMessage;
    public String error;
    public String messageTag;

    private WebSocketEvent(int type) {
        this.type = type;
    }

    public static WebSocketEvent stateChange(ConnectionState state) {
        WebSocketEvent event = new WebSocketEvent(TYPE_STATE_CHANGE);
        event.state = state;
        return event;
    }

    public static WebSocketEvent textMessage(String message) {
        WebSocketEvent event = new WebSocketEvent(TYPE_TEXT_MESSAGE);
        event.textMessage = message;
        return event;
    }

    public static WebSocketEvent binaryMessage(ByteString bytes) {
        WebSocketEvent event = new WebSocketEvent(TYPE_BINARY_MESSAGE);
        event.binaryMessage = bytes;
        return event;
    }

    public static WebSocketEvent error(String error) {
        WebSocketEvent event = new WebSocketEvent(TYPE_ERROR);
        event.error = error;
        return event;
    }

    public static WebSocketEvent sendSuccess(String tag) {
        WebSocketEvent event = new WebSocketEvent(TYPE_SEND_SUCCESS);
        event.messageTag = tag;
        return event;
    }

    public static WebSocketEvent sendFailure(String tag, String error) {
        WebSocketEvent event = new WebSocketEvent(TYPE_SEND_FAILURE);
        event.messageTag = tag;
        event.error = error;
        return event;
    }
}

4️⃣ 工具类

NetworkUtils.java

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



import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/**
 * 网络工具类
 */
public class NetworkUtils {
    /**
     * 判断网络是否可用
     */
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager cm = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        if (cm == null) return false;

        NetworkInfo[] info = cm.getAllNetworkInfo();
        if (info != null) {
            for (NetworkInfo networkInfo : info) {
                if (networkInfo.getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
            }
        }
        return false;
    }
}

ThreadUtils.java

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


import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程工具类
 */
public class ThreadUtils {
    private static final ExecutorService backgroundExecutor =
            Executors.newSingleThreadExecutor();
    private static final Handler mainHandler = new Handler(Looper.getMainLooper());

    /**
     * 在后台线程执行任务
     */
    public static void runOnBackground(Runnable task) {
        backgroundExecutor.execute(task);
    }

    /**
     * 在主线程执行任务
     */
    public static void runOnMainThread(Runnable task) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            task.run();
        } else {
            mainHandler.post(task);
        }
    }

    /**
     * 在主线程延迟执行任务
     */
    public static void runOnMainThreadDelayed(Runnable task, long delayMillis) {
        mainHandler.postDelayed(task, delayMillis);
    }

    /**
     * 移除主线程任务
     */
    public static void removeMainThreadTask(Runnable task) {
        mainHandler.removeCallbacks(task);
    }
}

5️⃣ 核心管理类

WebSocketManager.java

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



import android.content.*;
import android.net.ConnectivityManager;
import android.os.*;
import android.util.Log;

import androidx.annotation.NonNull;


import com.google.gson.*;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import okhttp3.*;
import okio.ByteString;

/**
 * WebSocket管理类(单例模式)
 * 功能:
 * 1. 自动重连
 * 2. 心跳检测
 * 3. 连接状态管理
 * 4. 网络状态监听自动重连
 * 5. 断线消息缓存与自动发送
 * 6. 消息缓存持久化到本地(SP)
 * 7. 消息数量限制和清理策略
 * 8. 消息时间限制(自动清理过期消息)
 * 9. 后台线程处理消息发送
 * 10. 消息发送状态回调
 * 11. 消息优先级支持
 * 12. 批量发送与节流控制
 * 13. 消息发送超时处理
 * 14. MVVM支持(LiveData)
 */
public class WebSocketManager {
    private static final String TAG = "WebSocketManager";
    private static final String PREF_NAME = "websocket_prefs";
    private static final String KEY_CONNECTED = "is_connected";
    private static final String KEY_URL = "ws_url";
    private static final String KEY_MESSAGE_QUEUE = "message_queue";
    private static final String KEY_BYTEMESSAGE_QUEUE = "byte_message_queue";

    // 默认最大缓存数量
    private static final int DEFAULT_MAX_TEXT_CACHE = 100;
    private static final int DEFAULT_MAX_BINARY_CACHE = 50;

    private static volatile WebSocketManager instance;

    private OkHttpClient client;
    private WebSocket webSocket;
    private WebSocketConfig config;
    private WebSocketCallback callback;
    private ConnectionState connectionState = ConnectionState.DISCONNECTED;

    private Context appContext;
    private NetworkChangeReceiver networkReceiver;

    // 持久化缓存队列(断线重连后发送)
    private Queue<TimedMessage> persistentMessageQueue = new ConcurrentLinkedQueue<>();
    private Queue<TimedMessage> persistentByteQueue = new ConcurrentLinkedQueue<>();


    // 内存中的优先级消息队列
    private PriorityBlockingQueue<PriorityMessage> sendQueue =
            new PriorityBlockingQueue<>(100, (msg1, msg2) -> {
                // 优先级高的先发送
                int priorityCompare = Integer.compare(
                        msg2.priority.ordinal(), msg1.priority.ordinal());
                if (priorityCompare != 0) return priorityCompare;
                // 优先级相同时,时间早的先发送
                return Long.compare(msg1.timestamp, msg2.timestamp);
            });

    // 发送线程池
    private ExecutorService sendExecutor = Executors.newSingleThreadExecutor();
    private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private ScheduledFuture<?> sendTaskFuture;

    // 发送超时管理
    private final Map<String, ScheduledFuture<?>> timeoutFutures = new ConcurrentHashMap<>();
    private final AtomicInteger messageIdGenerator = new AtomicInteger(0);

    // MVVM支持
    private final WebSocketLiveData webSocketLiveData = new WebSocketLiveData();

    private Gson gson = new Gson();

    private WebSocketManager() {
        client = new OkHttpClient.Builder()
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .build();
    }

    /**
     * 获取单例实例
     */
    public static WebSocketManager getInstance() {
        if (instance == null) {
            synchronized (WebSocketManager.class) {
                if (instance == null) {
                    instance = new WebSocketManager();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化WebSocket
     */
    public void init(Context context, WebSocketConfig config, WebSocketCallback callback) {
        this.appContext = context.getApplicationContext();
        this.config = config;
        this.callback = callback;

        // 设置默认值
        if (config.maxTextMessageCacheSize <= 0) {
            config.maxTextMessageCacheSize = DEFAULT_MAX_TEXT_CACHE;
        }
        if (config.maxBinaryMessageCacheSize <= 0) {
            config.maxBinaryMessageCacheSize = DEFAULT_MAX_BINARY_CACHE;
        }

        restoreConnectionState();
        loadPersistentMessageQueue();
        trimPersistentMessageQueue();

        registerNetworkListener();

        if (connectionState == ConnectionState.CONNECTED && config.url != null) {
            connect();
        }

        // 启动消息发送调度任务
        startSendScheduler();
    }

    /**
     * 获取LiveData(用于MVVM)
     */
    public WebSocketLiveData getWebSocketLiveData() {
        return webSocketLiveData;
    }

    /**
     * 注册网络状态监听广播
     */
    private void registerNetworkListener() {
        if (networkReceiver == null) {
            networkReceiver = new NetworkChangeReceiver();
            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
            appContext.registerReceiver(networkReceiver, filter);
        }
    }

    /**
     * 取消网络监听
     */
    public void unregisterNetworkListener() {
        if (networkReceiver != null) {
            try {
                appContext.unregisterReceiver(networkReceiver);
            } catch (Exception e) {
                log("Error unregistering network receiver", e);
            }
            networkReceiver = null;
        }
    }

    /**
     * 连接WebSocket
     */
    public void connect() {
        if (connectionState == ConnectionState.CONNECTED ||
                connectionState == ConnectionState.CONNECTING ||
                config == null || config.url == null) {
            return;
        }

        if (!NetworkUtils.isNetworkAvailable(appContext)) {
            logError("网络不可用,无法连接WebSocket");
            notifyError("网络不可用");
            scheduleReconnect();
            return;
        }

        setState(ConnectionState.CONNECTING);

        Request request = new Request.Builder().url(config.url).build();
        webSocket = client.newWebSocket(request, new InnerWebSocketListener());
        log("正在连接 WebSocket: " + config.url);
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        disconnect("主动断开连接");
    }

    /**
     * 断开连接(带原因)
     */
    public void disconnect(String reason) {
        if (webSocket != null) {
            webSocket.close(1000, reason);
            webSocket = null;
        }
        stopHeartbeat();
        cancelReconnect();
        setState(ConnectionState.DISCONNECTED);
    }

    /**
     * 发送文本消息(带优先级)
     */
    public String sendMessage(String text, MessagePriority priority, SendCallback callback) {
        return enqueueMessage(PriorityMessage.text(text, priority, callback));
    }

    /**
     * 发送二进制消息(带优先级)
     */
    public String sendMessage(ByteString bytes, MessagePriority priority, SendCallback callback) {
        return enqueueMessage(PriorityMessage.binary(bytes, priority, callback));
    }

    /**
     * 将消息加入发送队列
     */
    private String enqueueMessage(PriorityMessage msg) {
        // 生成唯一消息ID
        String messageId = "msg_" + System.currentTimeMillis() + "_" + messageIdGenerator.incrementAndGet();
        msg.tag = messageId;

        // 立即发送的消息直接在后台线程处理
        if (msg.priority == MessagePriority.IMMEDIATE) {
            ThreadUtils.runOnBackground(() -> sendImmediateMessage(msg));
        } else {
            // 其他优先级的消息加入队列
            sendQueue.offer(msg);

            // 如果是高优先级消息,唤醒发送线程
            if (msg.priority == MessagePriority.HIGH) {
                synchronized (sendQueue) {
                    sendQueue.notify();
                }
            }
        }

        // 设置发送超时
        scheduleSendTimeout(msg);

        return messageId;
    }

    /**
     * 立即发送消息
     */
    private void sendImmediateMessage(PriorityMessage msg) {
        if (connectionState != ConnectionState.CONNECTED || webSocket == null) {
            handleSendFailure(msg, "WebSocket未连接");
            return;
        }

        try {
            if (msg.isText()) {
                webSocket.send(msg.text);
            } else {
                webSocket.send(msg.bytes);
            }
            handleSendSuccess(msg);
        } catch (Exception e) {
            handleSendFailure(msg, e.getMessage());
        }
    }

    /**
     * 启动消息发送调度器
     */
    private void startSendScheduler() {
        stopSendScheduler();

        sendTaskFuture = scheduler.scheduleWithFixedDelay(() -> {
            try {
                processSendQueue();
            } catch (Exception e) {
                log("Error processing send queue", e);
            }
        }, 0, config.batchSendDelay, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止消息发送调度器
     */
    private void stopSendScheduler() {
        if (sendTaskFuture != null && !sendTaskFuture.isCancelled()) {
            sendTaskFuture.cancel(true);
            sendTaskFuture = null;
        }
    }

    /**
     * 处理发送队列
     */
    private void processSendQueue() {
        if (connectionState != ConnectionState.CONNECTED || webSocket == null) {
            return;
        }

        int batchCount = 0;
        PriorityMessage msg;

        // 处理高优先级消息(立即发送)
        while ((msg = sendQueue.peek()) != null &&
                msg.priority == MessagePriority.HIGH &&
                batchCount < config.batchSendSize) {
            msg = sendQueue.poll();
            sendMessageInternal(msg);
            batchCount++;
        }

        // 如果没有高优先级消息,处理普通优先级消息
        if (batchCount < config.batchSendSize) {
            while ((msg = sendQueue.poll()) != null && batchCount < config.batchSendSize) {
                sendMessageInternal(msg);
                batchCount++;
            }
        }
    }

    /**
     * 内部发送消息
     */
    private void sendMessageInternal(PriorityMessage msg) {
        try {
            if (msg.isText()) {
                webSocket.send(msg.text);
            } else {
                webSocket.send(msg.bytes);
            }
            handleSendSuccess(msg);
        } catch (Exception e) {
            handleSendFailure(msg, e.getMessage());
        }
    }

    /**
     * 处理发送成功
     */
    private void handleSendSuccess(PriorityMessage msg) {
        cancelSendTimeout(msg.tag);

        ThreadUtils.runOnMainThread(() -> {
            if (msg.callback != null) {
                try {
                    msg.callback.onSuccess();
                } catch (Exception e) {
                    log("Error in send success callback", e);
                }
            }

            // 通知LiveData
//            webSocketLiveData.postEvent(WebSocketEvent.sendSuccess(msg.tag));
        });
    }

    /**
     * 处理发送失败
     */
    private void handleSendFailure(PriorityMessage msg, String error) {
        cancelSendTimeout(msg.tag);

        ThreadUtils.runOnMainThread(() -> {
            if (msg.callback != null) {
                try {
                    msg.callback.onFailure(error);
                } catch (Exception e) {
                    log("Error in send failure callback", e);
                }
            }

            // 通知LiveData
            webSocketLiveData.postEvent(WebSocketEvent.sendFailure(msg.tag, error));
        });
    }

    /**
     * 设置发送超时
     */
    private void scheduleSendTimeout(PriorityMessage msg) {
        ScheduledFuture<?> future = scheduler.schedule(() -> {
            timeoutFutures.remove(msg.tag);
            handleSendFailure(msg, "发送超时");
        }, config.sendTimeout, TimeUnit.MILLISECONDS);

        timeoutFutures.put(msg.tag, future);
    }

    /**
     * 取消发送超时
     */
    private void cancelSendTimeout(String messageId) {
        ScheduledFuture<?> future = timeoutFutures.remove(messageId);
        if (future != null && !future.isDone()) {
            future.cancel(true);
        }
    }

    /**
     * 发送持久化缓存的消息
     */
    private void sendPersistentMessages() {
        // 发送文本消息
        while (!persistentMessageQueue.isEmpty()) {
            TimedMessage msg = persistentMessageQueue.poll();
            if (msg != null && msg.text != null) {
                webSocket.send(msg.text);
                log("发送持久化文本消息: " + msg.text);
            }
        }

        // 发送二进制消息
        while (!persistentByteQueue.isEmpty()) {
            TimedMessage msg = persistentByteQueue.poll();
            if (msg != null && msg.getByteString() != null) {
                webSocket.send(msg.getByteString());
                log("发送持久化二进制消息");
            }
        }

        savePersistentMessageQueue();
    }

    /**
     * 保存持久化消息队列到本地
     */
    private void savePersistentMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        // 保存文本消息队列
        List<TimedMessage> msgList = new ArrayList<>(persistentMessageQueue);
        editor.putString(KEY_MESSAGE_QUEUE, gson.toJson(msgList));

        // 保存二进制消息队列
        List<TimedMessage> byteList = new ArrayList<>(persistentByteQueue);
        editor.putString(KEY_BYTEMESSAGE_QUEUE, gson.toJson(byteList));

        editor.apply();
    }

    /**
     * 从本地加载持久化消息队列
     */
    private void loadPersistentMessageQueue() {
        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);

        // 加载文本消息队列
        String msgJson = sp.getString(KEY_MESSAGE_QUEUE, null);
        if (msgJson != null) {
            Type type = new TypeToken<List<TimedMessage>>() {}.getType();
            List<TimedMessage> msgList = gson.fromJson(msgJson, type);
            if (msgList != null) {
                persistentMessageQueue.addAll(msgList);
            }
        }

        // 加载二进制消息队列
        String byteJson = sp.getString(KEY_BYTEMESSAGE_QUEUE, null);
        if (byteJson != null) {
            Type type = new TypeToken<List<TimedMessage>>() {}.getType();
            List<TimedMessage> byteList = gson.fromJson(byteJson, type);
            if (byteList != null) {
                persistentByteQueue.addAll(byteList);
            }
        }

        log("从本地加载持久化消息: 文本消息 " + persistentMessageQueue.size() +
                " 条, 二进制消息 " + persistentByteQueue.size() + " 条");
    }

    /**
     * 裁剪持久化消息队列(按数量和时间)
     */
    private void trimPersistentMessageQueue() {
        trimTextMessageQueue();
        trimBinaryMessageQueue();
    }

    /**
     * 裁剪文本消息队列
     */
    private void trimTextMessageQueue() {
        // 移除过期消息
        long expiryTime = config.messageExpiryTime;
        while (!persistentMessageQueue.isEmpty() && persistentMessageQueue.peek().isExpired(expiryTime)) {
            TimedMessage removed = persistentMessageQueue.poll();
            log("文本消息过期,移除: " + removed.text);
        }

        // 移除超出数量限制的消息
        int maxSize = config.maxTextMessageCacheSize;
        while (persistentMessageQueue.size() > maxSize) {
            TimedMessage removed = persistentMessageQueue.poll();
            log("文本消息缓存超过上限,移除最早的消息: " + removed.text);
        }
    }

    /**
     * 裁剪二进制消息队列
     */
    private void trimBinaryMessageQueue() {
        // 移除过期消息
        long expiryTime = config.messageExpiryTime;
        while (!persistentByteQueue.isEmpty() && persistentByteQueue.peek().isExpired(expiryTime)) {
            persistentByteQueue.poll();
            log("二进制消息过期,移除");
        }

        // 移除超出数量限制的消息
        int maxSize = config.maxBinaryMessageCacheSize;
        while (persistentByteQueue.size() > maxSize) {
            persistentByteQueue.poll();
            log("二进制消息缓存超过上限,移除最早的消息");
        }
    }

    /**
     * 手动清理所有持久化缓存消息
     */
    public void clearPersistentMessages() {
        persistentMessageQueue.clear();
        persistentByteQueue.clear();
        savePersistentMessageQueue();
        log("手动清理所有持久化缓存消息");
    }

    /**
     * 启动心跳检测
     */
    private void startHeartbeat() {
        stopHeartbeat();
        if (!config.needHeartbeat) return;

        scheduler.scheduleAtFixedRate(() -> {
            try {
                if (connectionState == ConnectionState.CONNECTED && webSocket != null) {
                    webSocket.send(ByteString.EMPTY);
                    log("发送心跳包");
                }
            } catch (Exception e) {
                log("Error sending heartbeat", e);
            }
        }, config.heartbeatInterval, config.heartbeatInterval, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止心跳检测
     */
    private void stopHeartbeat() {
        // 心跳任务在scheduler中,会自动取消
    }

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!config.autoReconnect) return;

        scheduler.schedule(() -> {
            if (connectionState != ConnectionState.CONNECTED) {
                log("尝试重连 WebSocket");
                setState(ConnectionState.RECONNECTING);
                connect();
            }
        }, config.reconnectInterval, TimeUnit.MILLISECONDS);
    }

    /**
     * 取消重连
     */
    private void cancelReconnect() {
        // 重连任务在scheduler中,会自动取消
    }

    /**
     * 更新连接状态
     */
    private void setState(ConnectionState state) {
        if (this.connectionState == state) return;

        this.connectionState = state;
        saveConnectionState();

        ThreadUtils.runOnMainThread(() -> {
            if (callback != null) {
                try {
                    callback.onConnectionStateChanged(state);
                } catch (Exception e) {
                    log("Error in connection state callback", e);
                }
            }

            // 通知LiveData
            webSocketLiveData.postEvent(WebSocketEvent.stateChange(state));
        });
    }

    /**
     * 保存连接状态到本地
     */
    private void saveConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(KEY_CONNECTED, connectionState == ConnectionState.CONNECTED);
        editor.putString(KEY_URL, config != null ? config.url : null);
        editor.apply();
    }

    /**
     * 从本地恢复连接状态
     */
    private void restoreConnectionState() {
        if (appContext == null) return;

        SharedPreferences sp = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        boolean wasConnected = sp.getBoolean(KEY_CONNECTED, false);
        String savedUrl = sp.getString(KEY_URL, null);

        if (config != null && savedUrl != null) {
            config.url = savedUrl;
        }

        if (wasConnected) {
            connectionState = ConnectionState.DISCONNECTED; // 上次是连接状态,但现在需要重新连接
        } else {
            connectionState = ConnectionState.DISCONNECTED;
        }
    }

    /**
     * 通知错误
     */
    private void notifyError(String error) {
        ThreadUtils.runOnMainThread(() -> {
            if (callback != null) {
                try {
                    callback.onError(error);
                } catch (Exception e) {
                    log("Error in error callback", e);
                }
            }

            // 通知LiveData
            webSocketLiveData.postEvent(WebSocketEvent.error(error));
        });
    }

    /**
     * 日志输出
     */
    private void log(String message) {
        if (config != null && config.logEnabled) {
            Log.d(TAG, message);
        }
    }

    /**
     * 错误日志输出
     */
    private void logError(String message) {
        if (config != null && config.logEnabled) {
            Log.e(TAG, message);
        }
    }

    /**
     * 异常日志输出
     */
    private void log(String message, Throwable throwable) {
        if (config != null && config.logEnabled) {
            Log.e(TAG, message, throwable);
        }
    }

    /**
     * 释放资源
     */
    public void release() {
        disconnect("释放资源");
        unregisterNetworkListener();
        stopSendScheduler();
        scheduler.shutdown();
        sendExecutor.shutdown();
        clearPersistentMessages();
    }

    /**
     * 内部WebSocket监听(OkHttp的)
     */
    private class InnerWebSocketListener extends WebSocketListener {
        @Override
        public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
            super.onOpen(webSocket, response);
            log("WebSocket 连接成功");
            setState(ConnectionState.CONNECTED);
            startHeartbeat();

            // 连接成功后发送持久化缓存的消息
            sendPersistentMessages();
        }

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

            ThreadUtils.runOnMainThread(() -> {
                if (callback != null) {
                    try {
                        callback.onMessage(text);
                    } catch (Exception e) {
                        log("Error in text message callback", e);
                    }
                }

                // 通知LiveData
                webSocketLiveData.postEvent(WebSocketEvent.textMessage(text));
            });
        }

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

            ThreadUtils.runOnMainThread(() -> {
                if (callback != null) {
                    try {
                        callback.onMessage(bytes);
                    } catch (Exception e) {
                        log("Error in binary message callback", e);
                    }
                }

                // 通知LiveData
                webSocketLiveData.postEvent(WebSocketEvent.binaryMessage(bytes));
            });
        }

        @Override
        public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
            super.onClosed(webSocket, code, reason);
            log("WebSocket 关闭: " + reason);
            setState(ConnectionState.DISCONNECTED);
            stopHeartbeat();
            scheduleReconnect();
        }

        @Override
        public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            logError("WebSocket 连接失败: " + t.getMessage());
            notifyError(t.getMessage());
            setState(ConnectionState.DISCONNECTED);
            stopHeartbeat();
            scheduleReconnect();
        }
    }

    /**
     * 网络状态变化广播接收器
     */
    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                // 网络状态变化
                if (NetworkUtils.isNetworkAvailable(appContext)) {
                    log("网络已恢复");
                    if (connectionState == ConnectionState.DISCONNECTED ||
                            connectionState == ConnectionState.RECONNECTING) {
                        connect();
                    }
                } else {
                    log("网络已断开");
                    if (connectionState == ConnectionState.CONNECTED) {
                        disconnect("网络断开");
                    }
                }
            }
        }
    }
}

6️⃣ 注解(可选功能)

Priority.java

java

运行

复制代码
package com.example.socket.annotation;

import androidx.annotation.IntDef;
import com.example.socket.config.MessagePriority;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@IntDef({MessagePriority.LOW, MessagePriority.NORMAL, MessagePriority.HIGH, MessagePriority.IMMEDIATE})
@Retention(RetentionPolicy.SOURCE)
public @interface Priority {
}

🚀 使用示例

1. 基本使用(非 MVVM)

java

运行

复制代码
// 创建配置
WebSocketConfig config = new WebSocketConfig();
config.url = "ws://your.socket.url";
config.heartbeatInterval = 10_000;
config.reconnectInterval = 5_000;
config.autoReconnect = true;
config.needHeartbeat = true;
config.maxTextMessageCacheSize = 150;
config.maxBinaryMessageCacheSize = 80;
config.messageExpiryTime = 12 * 60 * 60 * 1000; // 12小时
config.sendTimeout = 8_000;
config.batchSendSize = 15;
config.batchSendDelay = 300;

// 初始化WebSocket
WebSocketManager.getInstance().init(
        getApplicationContext(),
        config,
        new WebSocketCallback() {
            @Override
            public void onConnectionStateChanged(ConnectionState state) {
                Log.d("WS", "连接状态变化: " + state);
            }
            
            @Override
            public void onMessage(String message) {
                Log.d("WS", "收到文本消息: " + message);
            }
            
            @Override
            public void onMessage(ByteString bytes) {
                Log.d("WS", "收到二进制消息: " + bytes.hex());
            }
            
            @Override
            public void onError(String error) {
                Log.e("WS", "错误: " + error);
            }
        }
);

// 连接WebSocket
WebSocketManager.getInstance().connect();

// 发送高优先级消息
WebSocketManager.getInstance().sendMessage(
        "这是高优先级消息", 
        MessagePriority.HIGH,
        new SendCallback() {
            @Override
            public void onSuccess() {
                Log.d("WS", "高优先级消息发送成功");
            }
            
            @Override
            public void onFailure(String error) {
                Log.e("WS", "高优先级消息发送失败: " + error);
            }
        }
);

// 发送普通消息
WebSocketManager.getInstance().sendMessage(
        "这是普通消息", 
        MessagePriority.NORMAL,
        null // 不需要回调
);

// 断开连接(不需要时)
// WebSocketManager.getInstance().disconnect();

// 释放资源(退出应用时)
// WebSocketManager.getInstance().release();

2. MVVM 中使用

java

运行

复制代码
// ViewModel中
public class MyViewModel extends ViewModel {
    private final WebSocketLiveData webSocketLiveData;
    
    public MyViewModel() {
        // 获取WebSocket LiveData
        webSocketLiveData = WebSocketManager.getInstance().getWebSocketLiveData();
    }
    
    public LiveData<WebSocketEvent> getWebSocketEvents() {
        return webSocketLiveData;
    }
    
    public void sendMessage(String text) {
        WebSocketManager.getInstance().sendMessage(
                text,
                MessagePriority.NORMAL,
                null
        );
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
        // 不需要释放WebSocket,由Application管理
    }
}

// Activity/Fragment中
public class MyFragment extends Fragment {
    private MyViewModel viewModel;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        
        // 观察WebSocket事件
        viewModel.getWebSocketEvents().observe(this, event -> {
            if (event == null) return;
            
            switch (event.type) {
                case WebSocketEvent.TYPE_STATE_CHANGE:
                    Log.d("WS", "连接状态: " + event.state);
                    break;
                case WebSocketEvent.TYPE_TEXT_MESSAGE:
                    Log.d("WS", "收到消息: " + event.textMessage);
                    // 更新UI
                    break;
                case WebSocketEvent.TYPE_ERROR:
                    Log.e("WS", "错误: " + event.error);
                    break;
                case WebSocketEvent.TYPE_SEND_SUCCESS:
                    Log.d("WS", "消息发送成功: " + event.messageTag);
                    break;
                case WebSocketEvent.TYPE_SEND_FAILURE:
                    Log.e("WS", "消息发送失败: " + event.messageTag + ", 原因: " + event.error);
                    break;
            }
        });
    }
    
    public void sendMessage(String text) {
        viewModel.sendMessage(text);
    }
}

📋 功能总结

后台线程处理消息发送 (避免 UI 阻塞)✅ 消息发送状态回调 (成功 / 失败)✅ 消息优先级支持 (重要消息优先)✅ 批量发送与节流控制 (优化性能)✅ 消息发送超时处理 (提高可靠性)✅ 断线重连策略优化 (智能重连)✅ 连接状态 LiveData (完美支持 MVVM)✅ 完整的缓存管理 (数量 + 时间限制)✅ 持久化缓存 (App 重启后恢复)✅ 网络状态监听 (自动重连)✅ 心跳检测 (保持连接)✅ 手动控制(连接 / 断开 / 清理缓存)

这个 WebSocket 封装库既可以在 MVVM 项目中使用,也可以作为独立模块使用,功能全面且易于扩展。

相关推荐
小趴菜82273 小时前
安卓人机验证View
android·java·前端
ajassi20003 小时前
开源 java android app 开发(十七)封库--混淆源码
android·java·开源
lichenyang4534 小时前
WebSocket实战:打造AI流式对话的实时通信基础
网络·websocket·网络协议
晨港飞燕4 小时前
Websocket+Redis实现微服务消息实时同步
redis·websocket·微服务
2501_916008894 小时前
JavaScript调试工具有哪些?常见问题与常用调试工具推荐
android·开发语言·javascript·小程序·uni-app·ecmascript·iphone
2501_929382655 小时前
AdGuard解锁订阅版高级版 安卓广告拦截器APP v4.11.63 / 4.13.7 Nightly MOD
android
vistaup5 小时前
android studio 无法运行java main()
android·java·android studio
sxczst7 小时前
Launcher3 如何实现长按后可拖动?
android
诺诺Okami8 小时前
Android Framework-WMS-Window移除
android