代码如下
package com.nyw.mvvmmode.net.socket;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* @author nyw
* @description 基于 OkHttp WebSocket 的 Socket 连接管理类
*/
public class SocketManager {
private static final String TAG = "SocketManager";
// 单例模式
private static volatile SocketManager instance;
private OkHttpClient okHttpClient;
private WebSocket webSocket;
// 连接状态
private boolean isConnected = false;
// 连接地址
private String socketUrl;
// 重连间隔时间(毫秒)
private static final long RECONNECT_INTERVAL = 5000;
private SocketListener socketListener;
private SocketManager() {
// 初始化 OkHttpClient
okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
.build();
}
public static SocketManager getInstance() {
if (instance == null) {
synchronized (SocketManager.class) {
if (instance == null) {
instance = new SocketManager();
}
}
}
return instance;
}
/**
* 设置 Socket 连接地址
*/
public void setSocketUrl(String url) {
this.socketUrl = url;
}
/**
* 连接 Socket
*/
public void connect() {
if (isConnected || socketUrl == null) {
return;
}
Request request = new Request.Builder().url(socketUrl).build();
webSocket = okHttpClient.newWebSocket(request, webSocketListener);
isConnected = true;
}
/**
* 断开连接
*/
public void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "主动断开连接");
webSocket = null;
}
isConnected = false;
}
/**
* 发送文本消息
*/
public void sendMessage(String message) {
if (webSocket != null && isConnected) {
webSocket.send(message);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
/**
* 发送二进制消息
*/
public void sendMessage(ByteString byteString) {
if (webSocket != null && isConnected) {
webSocket.send(byteString);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
/**
* 判断是否连接
*/
public boolean isConnected() {
return isConnected;
}
/**
* 设置 Socket 回调监听
*/
public void setSocketListener(SocketListener listener) {
this.socketListener = listener;
}
/**
* WebSocket 回调监听
*/
private final WebSocketListener webSocketListener = new WebSocketListener() {
@Override
public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
super.onOpen(webSocket, response);
Log.d(TAG, "Socket 连接成功");
isConnected = true;
if (socketListener != null) {
socketListener.onConnected();
}
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
super.onMessage(webSocket, text);
Log.d(TAG, "收到文本消息: " + text);
if (socketListener != null) {
socketListener.onMessage(text);
}
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
super.onMessage(webSocket, bytes);
Log.d(TAG, "收到二进制消息: " + bytes.hex());
if (socketListener != null) {
socketListener.onMessage(bytes);
}
}
@Override
public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosing(webSocket, code, reason);
Log.d(TAG, "Socket 正在关闭: " + reason);
isConnected = false;
if (socketListener != null) {
socketListener.onDisconnected(reason);
}
}
@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosed(webSocket, code, reason);
Log.d(TAG, "Socket 已关闭: " + reason);
isConnected = false;
if (socketListener != null) {
socketListener.onDisconnected(reason);
}
}
@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
Log.e(TAG, "Socket 连接失败: " + t.getMessage());
isConnected = false;
if (socketListener != null) {
socketListener.onError(t.getMessage());
}
// 自动重连
reconnect();
}
};
/**
* 自动重连
*/
private void reconnect() {
if (!isConnected) {
new Thread(() -> {
try {
Thread.sleep(RECONNECT_INTERVAL);
connect();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* Socket 回调接口
*/
public interface SocketListener {
void onConnected();
void onDisconnected(String reason);
void onMessage(String text);
void onMessage(ByteString bytes);
void onError(String error);
}
}
使用示例
// 初始化并连接
SocketManager.getInstance()
.setSocketUrl("ws://your.socket.url")
.setSocketListener(new SocketManager.SocketListener() {
@Override
public void onConnected() {
Log.d("SocketTest", "连接成功");
}
@Override
public void onDisconnected(String reason) {
Log.d("SocketTest", "断开连接: " + reason);
}
@Override
public void onMessage(String text) {
Log.d("SocketTest", "收到消息: " + text);
}
@Override
public void onMessage(ByteString bytes) {
Log.d("SocketTest", "收到二进制消息");
}
@Override
public void onError(String error) {
Log.e("SocketTest", "错误: " + error);
}
})
.connect();
// 发送消息
SocketManager.getInstance().sendMessage("Hello Socket Server");
// 断开连接
SocketManager.getInstance().disconnect();
功能特点
✅ 单例模式 ,全局唯一 Socket 连接✅ 自动重连 (连接失败或断开时自动尝试重连)✅ 支持文本和二进制消息 ✅ 统一回调接口 (连接、断开、接收消息、错误)✅ 线程安全(OkHttp 内部处理)
5. 注意事项
- WebSocket URL 协议用
ws://
(非加密)或wss://
(加密) - 如果需要在 UI 线程更新界面,回调中要切换到主线程(可以用 Handler 或 LiveData)
- 长连接要考虑服务器心跳检测机制
另一个封装,把这个 Socket 管理类和 MVVM 结合,用 LiveData 把消息分发到 ViewModel同时也可以支持单独不在mvvm使用
设计思路
- SocketManager 保留原来的
SocketListener
回调方式(支持独立使用) - 新增一个
LiveData
适配层,让 Socket 收到的消息自动 post 到 LiveData - ViewModel 中只需要持有 LiveData,UI 层观察它就能自动更新
改造后的 SocketManager(支持 LiveData)
package com.nyw.mvvmmode.net.socket;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* 基于 OkHttp WebSocket 的 Socket 连接管理类
* 支持 MVVM + LiveData,也可独立使用
*/
public class SocketManager {
private static final String TAG = "SocketManager";
private static volatile SocketManager instance;
private OkHttpClient okHttpClient;
private WebSocket webSocket;
private boolean isConnected = false;
private String socketUrl;
// 重连间隔
private static final long RECONNECT_INTERVAL = 5000;
// 独立使用的回调
private SocketListener socketListener;
// 用于 MVVM 的 LiveData
private final MutableLiveData<String> messageLiveData = new MutableLiveData<>();
private final MutableLiveData<ByteString> binaryLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
private SocketManager() {
okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
.build();
}
public static SocketManager getInstance() {
if (instance == null) {
synchronized (SocketManager.class) {
if (instance == null) {
instance = new SocketManager();
}
}
}
return instance;
}
public void setSocketUrl(String url) {
this.socketUrl = url;
}
public void connect() {
if (isConnected || socketUrl == null) return;
Request request = new Request.Builder().url(socketUrl).build();
webSocket = okHttpClient.newWebSocket(request, webSocketListener);
isConnected = true;
}
public void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "主动断开连接");
webSocket = null;
}
isConnected = false;
}
public void sendMessage(String message) {
if (webSocket != null && isConnected) {
webSocket.send(message);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
public void sendMessage(ByteString byteString) {
if (webSocket != null && isConnected) {
webSocket.send(byteString);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
public boolean isConnected() {
return isConnected;
}
// ====== 独立使用的回调 ======
public void setSocketListener(SocketListener listener) {
this.socketListener = listener;
}
// ====== MVVM LiveData 接口 ======
public LiveData<String> getMessageLiveData() {
return messageLiveData;
}
public LiveData<ByteString> getBinaryLiveData() {
return binaryLiveData;
}
public LiveData<Boolean> getConnectionLiveData() {
return connectionLiveData;
}
public LiveData<String> getErrorLiveData() {
return errorLiveData;
}
/**
* WebSocket 回调
*/
private final WebSocketListener webSocketListener = new WebSocketListener() {
@Override
public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
super.onOpen(webSocket, response);
Log.d(TAG, "Socket 连接成功");
isConnected = true;
// 回调
if (socketListener != null) socketListener.onConnected();
connectionLiveData.postValue(true);
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
super.onMessage(webSocket, text);
Log.d(TAG, "收到文本消息: " + text);
// 回调
if (socketListener != null) socketListener.onMessage(text);
messageLiveData.postValue(text);
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
super.onMessage(webSocket, bytes);
Log.d(TAG, "收到二进制消息: " + bytes.hex());
// 回调
if (socketListener != null) socketListener.onMessage(bytes);
binaryLiveData.postValue(bytes);
}
@Override
public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosing(webSocket, code, reason);
Log.d(TAG, "Socket 正在关闭: " + reason);
isConnected = false;
if (socketListener != null) socketListener.onDisconnected(reason);
connectionLiveData.postValue(false);
}
@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosed(webSocket, code, reason);
Log.d(TAG, "Socket 已关闭: " + reason);
isConnected = false;
if (socketListener != null) socketListener.onDisconnected(reason);
connectionLiveData.postValue(false);
}
@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
Log.e(TAG, "Socket 连接失败: " + t.getMessage());
isConnected = false;
if (socketListener != null) socketListener.onError(t.getMessage());
errorLiveData.postValue(t.getMessage());
// 自动重连
reconnect();
}
};
private void reconnect() {
if (!isConnected) {
new Thread(() -> {
try {
Thread.sleep(RECONNECT_INTERVAL);
connect();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* 独立使用的回调接口
*/
public interface SocketListener {
void onConnected();
void onDisconnected(String reason);
void onMessage(String text);
void onMessage(ByteString bytes);
void onError(String error);
}
}
在 MVVM 中使用(ViewModel + LiveData)
package com.nyw.mvvmmode.viewmodel;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.nyw.mvvmmode.net.socket.SocketManager;
import com.nyw.mvvmmode.utils.ToastUtils;
import okio.ByteString;
public class SocketViewModel extends AndroidViewModel {
private final SocketManager socketManager;
public SocketViewModel(@NonNull Application application) {
super(application);
socketManager = SocketManager.getInstance();
socketManager.setSocketUrl("ws://your.socket.url");
socketManager.connect();
}
// 暴露 LiveData 给 UI
public LiveData<String> getMessageLiveData() {
return socketManager.getMessageLiveData();
}
public LiveData<Boolean> getConnectionLiveData() {
return socketManager.getConnectionLiveData();
}
public LiveData<String> getErrorLiveData() {
return socketManager.getErrorLiveData();
}
// 发送消息
public void sendMessage(String msg) {
socketManager.sendMessage(msg);
}
@Override
protected void onCleared() {
super.onCleared();
socketManager.disconnect();
}
}
在 Activity/Fragment 中观察
public class SocketActivity extends AppCompatActivity {
private SocketViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
viewModel = new ViewModelProvider(this).get(SocketViewModel.class);
// 观察连接状态
viewModel.getConnectionLiveData().observe(this, isConnected -> {
if (isConnected) {
Toast.makeText(this, "Socket 连接成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Socket 已断开", Toast.LENGTH_SHORT).show();
}
});
// 观察文本消息
viewModel.getMessageLiveData().observe(this, msg -> {
Log.d("SocketActivity", "收到消息: " + msg);
// 更新UI
});
// 观察错误
viewModel.getErrorLiveData().observe(this, error -> {
Toast.makeText(this, "Socket 错误: " + error, Toast.LENGTH_SHORT).show();
});
}
}
单独使用(不依赖 MVVM)
SocketManager.getInstance()
.setSocketUrl("ws://your.socket.url")
.setSocketListener(new SocketManager.SocketListener() {
@Override
public void onConnected() {
Log.d("SocketTest", "连接成功");
}
@Override
public void onDisconnected(String reason) {
Log.d("SocketTest", "断开连接: " + reason);
}
@Override
public void onMessage(String text) {
Log.d("SocketTest", "收到消息: " + text);
}
@Override
public void onMessage(ByteString bytes) {
Log.d("SocketTest", "收到二进制消息");
}
@Override
public void onError(String error) {
Log.e("SocketTest", "错误: " + error);
}
})
.connect();
功能特点
✅ 支持 MVVM + LiveData ,UI 层只观察数据变化✅ 支持独立使用 ,通过 SocketListener
回调✅ 自动重连 ✅ 文本 & 二进制消息 都支持✅ 连接状态、错误信息 都有 LiveData 通知
✅ 这样你的 Socket 管理类就既能在 MVVM 架构中无缝使用,又能在普通 Activity / Service / 工具类中直接调用,非常灵活。
方式三封装。在上面的代码基础上,加上心跳检测机制,让 Socket 在长时间无消息时自动发送心跳包,防止被服务器断开。
SocketManager 中加上 心跳检测机制,这样长时间没收到服务器消息时,会自动发送心跳包保持连接,避免被服务器断开。
设计思路
- 心跳间隔 :每隔一段时间(如 30 秒)发送一次固定格式的心跳包(比如
"ping"
) - 心跳超时检测:如果超过一定时间(如 60 秒)没收到服务器的任何消息,则认为连接已断开,触发重连
- 使用
Handler
在主线程定时执行心跳任务 - 收到任何消息(包括心跳响应)都会重置超时计时器带心跳检测的 SocketManager(支持 MVVM LiveData)
带心跳检测的 SocketManager(支持 MVVM LiveData)
package com.nyw.mvvmmode.net.socket;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* 基于 OkHttp WebSocket 的 Socket 连接管理类
* 支持 MVVM + LiveData,也可独立使用,增加心跳检测机制
*/
public class SocketManager {
private static final String TAG = "SocketManager";
private static volatile SocketManager instance;
private OkHttpClient okHttpClient;
private WebSocket webSocket;
private boolean isConnected = false;
private String socketUrl;
// 重连间隔
private static final long RECONNECT_INTERVAL = 5000;
// 心跳间隔(30秒)
private static final long HEARTBEAT_INTERVAL = 30 * 1000;
// 心跳超时时间(60秒)
private static final long HEARTBEAT_TIMEOUT = 60 * 1000;
// 心跳消息内容
private static final String HEARTBEAT_MSG = "ping";
// 独立使用的回调
private SocketListener socketListener;
// 用于 MVVM 的 LiveData
private final MutableLiveData<String> messageLiveData = new MutableLiveData<>();
private final MutableLiveData<ByteString> binaryLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
// 心跳 & 超时检测 Handler
private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 1) { // 心跳任务
sendHeartbeat();
// 发送心跳后,启动超时检测
mainHandler.sendEmptyMessageDelayed(2, HEARTBEAT_TIMEOUT);
} else if (msg.what == 2) { // 超时任务
Log.e(TAG, "心跳超时,断开连接并尝试重连");
disconnect();
reconnect();
}
return true;
}
});
private SocketManager() {
okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
.build();
}
public static SocketManager getInstance() {
if (instance == null) {
synchronized (SocketManager.class) {
if (instance == null) {
instance = new SocketManager();
}
}
}
return instance;
}
public void setSocketUrl(String url) {
this.socketUrl = url;
}
public void connect() {
if (isConnected || socketUrl == null) return;
Request request = new Request.Builder().url(socketUrl).build();
webSocket = okHttpClient.newWebSocket(request, webSocketListener);
isConnected = true;
}
public void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "主动断开连接");
webSocket = null;
}
isConnected = false;
stopHeartbeat();
}
public void sendMessage(String message) {
if (webSocket != null && isConnected) {
webSocket.send(message);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
public void sendMessage(ByteString byteString) {
if (webSocket != null && isConnected) {
webSocket.send(byteString);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
public boolean isConnected() {
return isConnected;
}
// ====== 独立使用的回调 ======
public void setSocketListener(SocketListener listener) {
this.socketListener = listener;
}
// ====== MVVM LiveData 接口 ======
public LiveData<String> getMessageLiveData() {
return messageLiveData;
}
public LiveData<ByteString> getBinaryLiveData() {
return binaryLiveData;
}
public LiveData<Boolean> getConnectionLiveData() {
return connectionLiveData;
}
public LiveData<String> getErrorLiveData() {
return errorLiveData;
}
/**
* 发送心跳包
*/
private void sendHeartbeat() {
if (isConnected) {
Log.d(TAG, "发送心跳包: " + HEARTBEAT_MSG);
sendMessage(HEARTBEAT_MSG);
}
}
/**
* 启动心跳任务
*/
private void startHeartbeat() {
stopHeartbeat();
mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
}
/**
* 停止心跳任务
*/
private void stopHeartbeat() {
mainHandler.removeMessages(1);
mainHandler.removeMessages(2);
}
/**
* 重置心跳计时器(收到任何消息时调用)
*/
private void resetHeartbeat() {
stopHeartbeat();
mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
}
/**
* WebSocket 回调
*/
private final WebSocketListener webSocketListener = new WebSocketListener() {
@Override
public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
super.onOpen(webSocket, response);
Log.d(TAG, "Socket 连接成功");
isConnected = true;
// 回调
if (socketListener != null) socketListener.onConnected();
connectionLiveData.postValue(true);
// 启动心跳
startHeartbeat();
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
super.onMessage(webSocket, text);
Log.d(TAG, "收到文本消息: " + text);
// 重置心跳计时器
resetHeartbeat();
// 回调
if (socketListener != null) socketListener.onMessage(text);
messageLiveData.postValue(text);
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
super.onMessage(webSocket, bytes);
Log.d(TAG, "收到二进制消息: " + bytes.hex());
// 重置心跳计时器
resetHeartbeat();
// 回调
if (socketListener != null) socketListener.onMessage(bytes);
binaryLiveData.postValue(bytes);
}
@Override
public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosing(webSocket, code, reason);
Log.d(TAG, "Socket 正在关闭: " + reason);
isConnected = false;
if (socketListener != null) socketListener.onDisconnected(reason);
connectionLiveData.postValue(false);
stopHeartbeat();
}
@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosed(webSocket, code, reason);
Log.d(TAG, "Socket 已关闭: " + reason);
isConnected = false;
if (socketListener != null) socketListener.onDisconnected(reason);
connectionLiveData.postValue(false);
stopHeartbeat();
}
@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
Log.e(TAG, "Socket 连接失败: " + t.getMessage());
isConnected = false;
if (socketListener != null) socketListener.onError(t.getMessage());
errorLiveData.postValue(t.getMessage());
stopHeartbeat();
// 自动重连
reconnect();
}
};
/**
* 自动重连
*/
private void reconnect() {
if (!isConnected) {
new Thread(() -> {
try {
Thread.sleep(RECONNECT_INTERVAL);
connect();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* 独立使用的回调接口
*/
public interface SocketListener {
void onConnected();
void onDisconnected(String reason);
void onMessage(String text);
void onMessage(ByteString bytes);
void onError(String error);
}
}
心跳机制说明
- 启动心跳 :在
onOpen
回调中调用startHeartbeat()
- 发送心跳 :每隔
HEARTBEAT_INTERVAL
(30 秒)发送一次"ping"
- 超时检测 :如果超过
HEARTBEAT_TIMEOUT
(60 秒)没有收到任何消息,就触发重连 - 重置心跳:每次收到服务器消息(文本 / 二进制)都会重置计时器
- 停止心跳 :连接断开或关闭时调用
stopHeartbeat()
MVVM 使用方式(不变)
java
运行
viewModel.getMessageLiveData().observe(this, msg -> {
Log.d("SocketActivity", "收到消息: " + msg);
});
单独使用方式(不变)
SocketManager.getInstance()
.setSocketUrl("ws://your.socket.url")
.setSocketListener(new SocketManager.SocketListener() {
@Override
public void onConnected() {
Log.d("SocketTest", "连接成功");
}
@Override
public void onDisconnected(String reason) {
Log.d("SocketTest", "断开连接: " + reason);
}
@Override
public void onMessage(String text) {
Log.d("SocketTest", "收到消息: " + text);
}
@Override
public void onMessage(ByteString bytes) {}
@Override
public void onError(String error) {}
})
.connect();
✅ 这样你的 SocketManager 现在支持:
- MVVM + LiveData
- 独立使用
- 自动重连
- 心跳检测
封装4,在加一个 "后台保活" 功能, 在 Service 中运行 Socket 连接,这样即使 App 退到后台,Socket 也能保持连接。当前的 SocketManager 基础上增加一个 后台保活功能 ,通过 Service 来运行 Socket 连接,这样即使 App 退到后台,也能保持长连接。
设计思路
- 创建一个 SocketService 继承自
Service
- 在
SocketService
中持有SocketManager
实例,并在onCreate()
启动连接 - 在
onDestroy()
断开连接 - 使用 startForeground() 提升 Service 优先级,减少被系统杀死的概率
- 在 Application 或 Activity 中启动 Service
Socket 保活 Service
package com.nyw.mvvmmode.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.nyw.mvvmmode.MainActivity;
import com.nyw.mvvmmode.R;
import com.nyw.mvvmmode.net.socket.SocketManager;
/**
* Socket 后台保活 Service
*/
public class SocketService extends android.app.Service {
private static final String TAG = "SocketService";
private static final int NOTIFICATION_ID = 1001;
private static final String CHANNEL_ID = "socket_channel";
private SocketManager socketManager;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "SocketService onCreate");
// 获取 SocketManager 实例
socketManager = SocketManager.getInstance();
socketManager.setSocketUrl("ws://your.socket.url");
// 启动 Socket 连接
socketManager.connect();
// 启动前台服务,提高优先级
startForeground(NOTIFICATION_ID, createNotification());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "SocketService onStartCommand");
return START_STICKY; // 服务被杀死后自动重启
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "SocketService onDestroy");
// 断开 Socket 连接
if (socketManager != null) {
socketManager.disconnect();
}
}
/**
* 创建前台通知
*/
private Notification createNotification() {
// 创建通知渠道(Android 8.0+ 必须)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Socket 保活",
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
// 点击通知回到 MainActivity
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Socket 连接中")
.setContentText("保持后台实时通信")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.build();
}
}
在 AndroidManifest.xml 注册 Service
<service
android:name=".service.SocketService"
android:foregroundServiceType="dataSync"
android:exported="false" />
启动 Service 的方式
从 Application 启动(推荐)
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 启动 Socket 保活服务
Intent intent = new Intent(this, SocketService.class);
startForegroundService(intent);
}
}
从 Activity 启动
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(new Intent(this, SocketService.class));
} else {
startService(new Intent(this, SocketService.class));
}
后台保活原理
- 前台服务 :通过
startForeground()
显示一个常驻通知,提高 Service 优先级 - START_STICKY:Service 被系统杀死后会尝试重启
- 数据同步类型 :
foregroundServiceType="dataSync"
表示这是数据同步服务,符合 Android 10+ 后台限制要求
5. 注意事项
- Android 8.0+ 必须使用
startForegroundService()
并在 5 秒内调用startForeground()
- 通知栏图标和文字是必须的,否则会崩溃
- 某些手机厂商(如华为、小米)可能需要手动开启后台权限
- 长时间后台运行仍可能受到系统电量优化限制
✅ 这样你的 SocketManager 现在支持:
- MVVM + LiveData
- 独立使用
- 自动重连
- 心跳检测
- 后台保活(Service)
封装5,把 SocketService 和 SocketManager 整合得更紧密,比如在 Service 中自动管理连接状态,并且在 UI 中也能通过 LiveData 观察连接和消息。
把 SocketService 和 SocketManager 整合得更紧密,这样 Service 会负责 Socket 的生命周期管理,UI 层依然可以通过 LiveData 观察连接状态和消息,即使 Activity 退出,Socket 也能在后台保持连接。
设计思路
- SocketManager 保留单例模式,但不再自己管理连接,而是提供连接 / 断开方法
- SocketService 启动时调用
SocketManager.connect()
,销毁时调用SocketManager.disconnect()
- Service 与 Manager 之间通过 回调 通信
- UI 层通过
SocketManager
的 LiveData 观察数据,不直接依赖 Service - 后台保活通过
startForeground()
实现
整合后的 SocketManager(支持 LiveData + 回调)
package com.nyw.mvvmmode.net.socket;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* 基于 OkHttp WebSocket 的 Socket 连接管理类
* 支持 MVVM + LiveData,也可独立使用,带心跳检测
*/
public class SocketManager {
private static final String TAG = "SocketManager";
private static volatile SocketManager instance;
private OkHttpClient okHttpClient;
private WebSocket webSocket;
private boolean isConnected = false;
private String socketUrl;
// 重连间隔
private static final long RECONNECT_INTERVAL = 5000;
// 心跳间隔(30秒)
private static final long HEARTBEAT_INTERVAL = 30 * 1000;
// 心跳超时时间(60秒)
private static final long HEARTBEAT_TIMEOUT = 60 * 1000;
// 心跳消息内容
private static final String HEARTBEAT_MSG = "ping";
// 独立使用的回调
private SocketListener socketListener;
// 用于 MVVM 的 LiveData
private final MutableLiveData<String> messageLiveData = new MutableLiveData<>();
private final MutableLiveData<ByteString> binaryLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
// 心跳 & 超时检测 Handler
private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 1) { // 心跳任务
sendHeartbeat();
// 发送心跳后,启动超时检测
mainHandler.sendEmptyMessageDelayed(2, HEARTBEAT_TIMEOUT);
} else if (msg.what == 2) { // 超时任务
Log.e(TAG, "心跳超时,断开连接并尝试重连");
disconnect();
reconnect();
}
return true;
}
});
private SocketManager() {
okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时
.build();
}
public static SocketManager getInstance() {
if (instance == null) {
synchronized (SocketManager.class) {
if (instance == null) {
instance = new SocketManager();
}
}
}
return instance;
}
public void setSocketUrl(String url) {
this.socketUrl = url;
}
public void connect() {
if (isConnected || socketUrl == null) return;
Request request = new Request.Builder().url(socketUrl).build();
webSocket = okHttpClient.newWebSocket(request, webSocketListener);
isConnected = true;
}
public void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "主动断开连接");
webSocket = null;
}
isConnected = false;
stopHeartbeat();
}
public void sendMessage(String message) {
if (webSocket != null && isConnected) {
webSocket.send(message);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
public void sendMessage(ByteString byteString) {
if (webSocket != null && isConnected) {
webSocket.send(byteString);
} else {
Log.e(TAG, "Socket 未连接,无法发送消息");
}
}
public boolean isConnected() {
return isConnected;
}
// ====== 独立使用的回调 ======
public void setSocketListener(SocketListener listener) {
this.socketListener = listener;
}
// ====== MVVM LiveData 接口 ======
public LiveData<String> getMessageLiveData() {
return messageLiveData;
}
public LiveData<ByteString> getBinaryLiveData() {
return binaryLiveData;
}
public LiveData<Boolean> getConnectionLiveData() {
return connectionLiveData;
}
public LiveData<String> getErrorLiveData() {
return errorLiveData;
}
/**
* 发送心跳包
*/
private void sendHeartbeat() {
if (isConnected) {
Log.d(TAG, "发送心跳包: " + HEARTBEAT_MSG);
sendMessage(HEARTBEAT_MSG);
}
}
/**
* 启动心跳任务
*/
private void startHeartbeat() {
stopHeartbeat();
mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
}
/**
* 停止心跳任务
*/
private void stopHeartbeat() {
mainHandler.removeMessages(1);
mainHandler.removeMessages(2);
}
/**
* 重置心跳计时器(收到任何消息时调用)
*/
private void resetHeartbeat() {
stopHeartbeat();
mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);
}
/**
* WebSocket 回调
*/
private final WebSocketListener webSocketListener = new WebSocketListener() {
@Override
public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
super.onOpen(webSocket, response);
Log.d(TAG, "Socket 连接成功");
isConnected = true;
// 回调
if (socketListener != null) socketListener.onConnected();
connectionLiveData.postValue(true);
// 启动心跳
startHeartbeat();
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
super.onMessage(webSocket, text);
Log.d(TAG, "收到文本消息: " + text);
// 重置心跳计时器
resetHeartbeat();
// 回调
if (socketListener != null) socketListener.onMessage(text);
messageLiveData.postValue(text);
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
super.onMessage(webSocket, bytes);
Log.d(TAG, "收到二进制消息: " + bytes.hex());
// 重置心跳计时器
resetHeartbeat();
// 回调
if (socketListener != null) socketListener.onMessage(bytes);
binaryLiveData.postValue(bytes);
}
@Override
public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosing(webSocket, code, reason);
Log.d(TAG, "Socket 正在关闭: " + reason);
isConnected = false;
if (socketListener != null) socketListener.onDisconnected(reason);
connectionLiveData.postValue(false);
stopHeartbeat();
}
@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
super.onClosed(webSocket, code, reason);
Log.d(TAG, "Socket 已关闭: " + reason);
isConnected = false;
if (socketListener != null) socketListener.onDisconnected(reason);
connectionLiveData.postValue(false);
stopHeartbeat();
}
@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
Log.e(TAG, "Socket 连接失败: " + t.getMessage());
isConnected = false;
if (socketListener != null) socketListener.onError(t.getMessage());
errorLiveData.postValue(t.getMessage());
stopHeartbeat();
// 自动重连
reconnect();
}
};
/**
* 自动重连
*/
private void reconnect() {
if (!isConnected) {
new Thread(() -> {
try {
Thread.sleep(RECONNECT_INTERVAL);
connect();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* 独立使用的回调接口
*/
public interface SocketListener {
void onConnected();
void onDisconnected(String reason);
void onMessage(String text);
void onMessage(ByteString bytes);
void onError(String error);
}
}
整合后的 SocketService
package com.nyw.mvvmmode.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.Observer;
import com.nyw.mvvmmode.MainActivity;
import com.nyw.mvvmmode.R;
import com.nyw.mvvmmode.net.socket.SocketManager;
/**
* Socket 后台保活 Service(与 SocketManager 紧密整合)
*/
public class SocketService extends android.app.Service {
private static final String TAG = "SocketService";
private static final int NOTIFICATION_ID = 1001;
private static final String CHANNEL_ID = "socket_channel";
private SocketManager socketManager;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "SocketService onCreate");
// 获取 SocketManager 实例
socketManager = SocketManager.getInstance();
socketManager.setSocketUrl("ws://your.socket.url");
// 观察连接状态变化(可用于更新通知)
socketManager.getConnectionLiveData().observeForever(connectionObserver);
// 启动 Socket 连接
socketManager.connect();
// 启动前台服务,提高优先级
startForeground(NOTIFICATION_ID, createNotification());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "SocketService onStartCommand");
return START_STICKY; // 服务被杀死后自动重启
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "SocketService onDestroy");
// 移除观察者
socketManager.getConnectionLiveData().removeObserver(connectionObserver);
// 断开 Socket 连接
socketManager.disconnect();
}
/**
* 创建前台通知
*/
private Notification createNotification() {
// 创建通知渠道(Android 8.0+ 必须)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Socket 保活",
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
// 点击通知回到 MainActivity
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Socket 连接中")
.setContentText("保持后台实时通信")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.build();
}
/**
* 连接状态观察者
*/
private final Observer<Boolean> connectionObserver = isConnected -> {
if (isConnected != null && isConnected) {
Log.d(TAG, "Socket 连接成功(Service 观察到)");
// 可更新通知内容
} else {
Log.d(TAG, "Socket 已断开(Service 观察到)");
}
};
}
启动 Service
// 在 Application 或 Activity 中启动
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, SocketService.class));
} else {
context.startService(new Intent(context, SocketService.class));
}
UI 层通过 ViewModel 观察
public class SocketViewModel extends AndroidViewModel {
private final SocketManager socketManager;
public SocketViewModel(@NonNull Application application) {
super(application);
socketManager = SocketManager.getInstance();
}
public LiveData<String> getMessageLiveData() {
return socketManager.getMessageLiveData();
}
public LiveData<Boolean> getConnectionLiveData() {
return socketManager.getConnectionLiveData();
}
public void sendMessage(String msg) {
socketManager.sendMessage(msg);
}
}
整合后的优势
✅ Service 负责 Socket 生命周期 (启动 / 销毁 / 重连)✅ UI 层完全解耦 ,只通过 LiveData 观察数据✅ 后台保活 ,通过前台服务提高优先级✅ 心跳检测 保持连接稳定✅ 自动重连 机制✅ 支持独立使用(不依赖 MVVM)
✅ 这样你的长连接模块就非常完善了,可以直接在项目中使用,而且结构清晰,Service 负责后台运行,Manager 负责业务逻辑,UI 层只管展示数据。
封装6,再加上 "网络状态变化自动重连" 功能, 当设备网络从无到有时,自动重新连接 Socket。SocketManager 增加一个 网络状态变化自动重连 的功能,这样当设备从无网络切换到有网络时,Socket 会自动尝试重新连接,保证长连接的稳定性。
设计思路
- 使用
ConnectivityManager.NetworkCallback
监听网络状态变化(Android 7.0+ 推荐) - 当网络从 无连接 → 有连接 时,如果 Socket 当前未连接,则调用
connect()
- 当网络从 有连接 → 无连接 时,断开 Socket 连接
- 在
SocketManager
初始化时注册网络监听,在销毁时取消监听 - 兼容旧版本系统(可使用广播监听网络变化)
网络状态监听工具类
package com.nyw.mvvmmode.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
/**
* 网络工具类(判断网络状态 + 监听网络变化)
*/
public class NetworkUtils {
/**
* 判断是否有网络连接
*/
public static boolean isNetworkConnected(Context context) {
if (context == null) return false;
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Network network = cm.getActiveNetwork();
if (network == null) return false;
NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
return capabilities != null && (
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
);
} else {
NetworkInfo info = cm.getActiveNetworkInfo();
return info != null && info.isConnected();
}
}
/**
* 注册网络变化监听(Android 7.0+)
*/
public static void registerNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
cm.registerDefaultNetworkCallback(callback);
}
}
}
/**
* 取消网络变化监听
*/
public static void unregisterNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
cm.unregisterNetworkCallback(callback);
}
}
}
}
修改 SocketManager,增加网络监听
在 SocketManager
中添加以下代码:
// 在 SocketManager 中添加
private ConnectivityManager.NetworkCallback networkCallback;
private SocketManager() {
okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.build();
// 初始化网络监听
initNetworkCallback();
}
/**
* 初始化网络监听
*/
private void initNetworkCallback() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
Log.d(TAG, "网络已恢复,尝试重连 Socket");
if (!isConnected()) {
connect();
}
}
@Override
public void onLost(Network network) {
super.onLost(network);
Log.d(TAG, "网络已断开,关闭 Socket");
if (isConnected()) {
disconnect();
}
}
};
}
}
/**
* 注册网络监听(建议在 Application 初始化时调用)
*/
public void registerNetworkListener(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NetworkUtils.registerNetworkCallback(context, networkCallback);
}
}
/**
* 取消网络监听
*/
public void unregisterNetworkListener(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NetworkUtils.unregisterNetworkCallback(context, networkCallback);
}
}
最终完整代码集合如下
完整的 Socket 长连接管理代码 ,支持到 Android 16(Android 4.1),包含我们之前讨论的所有功能:
- MVVM + LiveData 支持
- 自动重连(连接失败 / 断开)
- 心跳检测(定时发送 ping)
- 后台保活(前台服务)
- 网络状态变化自动重连
- 连接状态持久化(SharedPreferences)
1. 目录结构
plaintext
app/src/main/java/com/yourpackage/socket/
├── SocketManager.java // 核心管理类(单例)
├── SocketService.java // 前台服务保活
├── NetworkUtils.java // 网络工具类(判断网络状态 + 监听网络变化)
├── MyApplication.java // 全局 Application
└── viewmodel/
└── SocketViewModel.java // ViewModel 示例
2. NetworkUtils.java(网络工具类)
package com.yourpackage.socket;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
/**
* 网络工具类
* 支持 Android 16+
*/
public class NetworkUtils {
/**
* 判断是否有网络连接
*/
public static boolean isNetworkConnected(Context context) {
if (context == null) return false;
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Network network = cm.getActiveNetwork();
if (network == null) return false;
NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
return capabilities != null && (
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
);
} else {
NetworkInfo info = cm.getActiveNetworkInfo();
return info != null && info.isConnected();
}
}
/**
* 注册网络变化监听(Android 7.0+)
*/
public static void registerNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
cm.registerDefaultNetworkCallback(callback);
}
}
}
/**
* 取消网络变化监听
*/
public static void unregisterNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
cm.unregisterNetworkCallback(callback);
}
}
}
}
3. SocketManager.java(核心管理类)
package com.yourpackage.socket;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Network;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* Socket 长连接管理类(单例模式)
* 功能:
* 1. 自动重连
* 2. 心跳检测
* 3. 网络状态监听
* 4. 连接状态持久化
* 5. LiveData 数据分发
*/
public class SocketManager {
private static final String TAG = "SocketManager";
private static final String PREF_NAME = "socket_prefs";
private static final String KEY_CONNECTED = "is_connected";
private static final String KEY_SOCKET_URL = "socket_url";
private static SocketManager instance;
private OkHttpClient okHttpClient;
private WebSocket webSocket;
private String socketUrl;
private boolean isConnected = false;
private MutableLiveData<Boolean> connectionLiveData = new MutableLiveData<>();
private MutableLiveData<String> messageLiveData = new MutableLiveData<>();
private MutableLiveData<String> errorLiveData = new MutableLiveData<>();
private Handler mainHandler = new Handler(Looper.getMainLooper());
private Runnable heartbeatRunnable;
private static final long HEARTBEAT_INTERVAL = 10 * 1000; // 10秒
private ConnectivityManager.NetworkCallback networkCallback;
private SocketManager() {
okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.build();
initNetworkCallback();
restoreConnectionState();
}
public static synchronized SocketManager getInstance() {
if (instance == null) {
instance = new SocketManager();
}
return instance;
}
public void setSocketUrl(String url) {
this.socketUrl = url;
}
public LiveData<Boolean> getConnectionLiveData() {
return connectionLiveData;
}
public LiveData<String> getMessageLiveData() {
return messageLiveData;
}
public LiveData<String> getErrorLiveData() {
return errorLiveData;
}
public boolean isConnected() {
return isConnected;
}
/**
* 连接 WebSocket
*/
public void connect() {
if (isConnected || socketUrl == null) return;
Request request = new Request.Builder().url(socketUrl).build();
webSocket = okHttpClient.newWebSocket(request, webSocketListener);
updateConnectionState(true);
}
/**
* 断开连接
*/
public void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "主动断开连接");
webSocket = null;
}
updateConnectionState(false);
stopHeartbeat();
}
/**
* 发送消息
*/
public void sendMessage(String text) {
if (webSocket != null) {
webSocket.send(text);
}
}
/**
* 初始化网络监听
*/
private void initNetworkCallback() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
if (!isConnected()) {
connect();
}
}
@Override
public void onLost(Network network) {
super.onLost(network);
if (isConnected()) {
disconnect();
}
}
};
}
}
/**
* 注册网络监听
*/
public void registerNetworkListener(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NetworkUtils.registerNetworkCallback(context, networkCallback);
}
}
/**
* 取消网络监听
*/
public void unregisterNetworkListener(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NetworkUtils.unregisterNetworkCallback(context, networkCallback);
}
}
/**
* 保存连接状态
*/
private void saveConnectionState() {
Context context = MyApplication.getContext();
if (context == null) return;
SharedPreferences sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean(KEY_CONNECTED, isConnected);
editor.putString(KEY_SOCKET_URL, socketUrl);
editor.apply();
}
/**
* 恢复连接状态
*/
private void restoreConnectionState() {
Context context = MyApplication.getContext();
if (context == null) return;
SharedPreferences sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
isConnected = sp.getBoolean(KEY_CONNECTED, false);
socketUrl = sp.getString(KEY_SOCKET_URL, null);
if (isConnected && socketUrl != null) {
connect();
}
}
/**
* 更新连接状态
*/
private void updateConnectionState(boolean connected) {
isConnected = connected;
connectionLiveData.postValue(connected);
saveConnectionState();
}
/**
* 启动心跳
*/
private void startHeartbeat() {
stopHeartbeat();
heartbeatRunnable = new Runnable() {
@Override
public void run() {
if (webSocket != null) {
webSocket.send(ByteString.EMPTY);
}
mainHandler.postDelayed(this, HEARTBEAT_INTERVAL);
}
};
mainHandler.postDelayed(heartbeatRunnable, HEARTBEAT_INTERVAL);
}
/**
* 停止心跳
*/
private void stopHeartbeat() {
if (heartbeatRunnable != null) {
mainHandler.removeCallbacks(heartbeatRunnable);
heartbeatRunnable = null;
}
}
/**
* WebSocket 回调监听
*/
private WebSocketListener webSocketListener = new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
updateConnectionState(true);
startHeartbeat();
}
@Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
messageLiveData.postValue(text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
// 处理二进制消息
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
updateConnectionState(false);
stopHeartbeat();
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
errorLiveData.postValue(t.getMessage());
updateConnectionState(false);
stopHeartbeat();
// 自动重连
mainHandler.postDelayed(() -> connect(), 5000);
}
};
}
4. SocketService.java(前台服务保活)
package com.yourpackage.socket;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;
/**
* 前台服务保活
*/
public class SocketService extends Service {
private static final int NOTIFICATION_ID = 1001;
private static final String CHANNEL_ID = "socket_channel";
@Override
public void onCreate() {
super.onCreate();
startForeground(NOTIFICATION_ID, createNotification());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 确保Socket连接
if (!SocketManager.getInstance().isConnected()) {
SocketManager.getInstance().connect();
}
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 创建前台通知
*/
private Notification createNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Socket连接",
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Socket连接中")
.setContentText("保持后台连接")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
}
}
5. MyApplication.java(全局 Application)
java
运行
package com.yourpackage.socket;
import android.app.Application;
import android.content.Context;
/**
* 全局 Application
*/
public class MyApplication extends Application {
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
super.onCreate();
context = this;
// 初始化SocketManager
SocketManager.getInstance()
.setSocketUrl("ws://your.socket.url")
.registerNetworkListener(this);
// 启动Socket保活服务
startSocketService();
}
/**
* 启动Socket服务
*/
private void startSocketService() {
Intent intent = new Intent(this, SocketService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
@Override
public void onTerminate() {
super.onTerminate();
SocketManager.getInstance().unregisterNetworkListener(this);
}
}
6. SocketViewModel.java(ViewModel 示例)
java
运行
package com.yourpackage.socket.viewmodel;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.yourpackage.socket.SocketManager;
/**
* Socket ViewModel
*/
public class SocketViewModel extends AndroidViewModel {
private SocketManager socketManager;
public SocketViewModel(@NonNull Application application) {
super(application);
socketManager = SocketManager.getInstance();
}
public LiveData<Boolean> getConnectionState() {
return socketManager.getConnectionLiveData();
}
public LiveData<String> getMessages() {
return socketManager.getMessageLiveData();
}
public LiveData<String> getErrors() {
return socketManager.getErrorLiveData();
}
public void sendMessage(String message) {
socketManager.sendMessage(message);
}
}
7. 权限配置(AndroidManifest.xml)
xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".socket.MyApplication"
...>
<service
android:name=".socket.SocketService"
android:foregroundServiceType="dataSync"
android:exported="false" />
<!-- MainActivity -->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
✅ 这样你就有了一套完整的 Socket 长连接解决方案:
- 支持 Android 16+
- 自动重连
- 心跳检测
- 网络状态监听
- 连接状态持久化
- MVVM + LiveData 架构
- 前台服务保活
