android 基于okhttp的socket封装 - 实践
代码如下
package com.nyw.mvvmmode.net.socket;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/*** @author nyw* @description 基于 OkHttp WebSocket 的 Socket 连接管理类*/
public class SocketManager {private static final String TAG = "SocketManager";// 单例模式private static volatile SocketManager instance;private OkHttpClient okHttpClient;private WebSocket webSocket;// 连接状态private boolean isConnected = false;// 连接地址private String socketUrl;// 重连间隔时间(毫秒)private static final long RECONNECT_INTERVAL = 5000;private SocketListener socketListener;private SocketManager() {// 初始化 OkHttpClientokHttpClient = 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() {@Overridepublic void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {super.onOpen(webSocket, response);Log.d(TAG, "Socket 连接成功");isConnected = true;if (socketListener != null) {socketListener.onConnected();}}@Overridepublic void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {super.onMessage(webSocket, text);Log.d(TAG, "收到文本消息: " + text);if (socketListener != null) {socketListener.onMessage(text);}}@Overridepublic void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {super.onMessage(webSocket, bytes);Log.d(TAG, "收到二进制消息: " + bytes.hex());if (socketListener != null) {socketListener.onMessage(bytes);}}@Overridepublic 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);}}@Overridepublic 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);}}@Overridepublic 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() {@Overridepublic void onConnected() {Log.d("SocketTest", "连接成功");}@Overridepublic void onDisconnected(String reason) {Log.d("SocketTest", "断开连接: " + reason);}@Overridepublic void onMessage(String text) {Log.d("SocketTest", "收到消息: " + text);}@Overridepublic void onMessage(ByteString bytes) {Log.d("SocketTest", "收到二进制消息");}@Overridepublic 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 的 LiveDataprivate final MutableLiveData messageLiveData = new MutableLiveData<>();private final MutableLiveData binaryLiveData = new MutableLiveData<>();private final MutableLiveData connectionLiveData = new MutableLiveData<>();private final MutableLiveData errorLiveData = new MutableLiveData<>();private SocketManager() {okHttpClient = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时.build();}public static SocketManager getInstance() {if (instance == null) {synchronized (SocketManager.class) {if (instance == null) {instance = new SocketManager();}}}return instance;}public void setSocketUrl(String url) {this.socketUrl = url;}public void connect() {if (isConnected || socketUrl == null) return;Request request = new Request.Builder().url(socketUrl).build();webSocket = okHttpClient.newWebSocket(request, webSocketListener);isConnected = true;}public void disconnect() {if (webSocket != null) {webSocket.close(1000, "主动断开连接");webSocket = null;}isConnected = false;}public void sendMessage(String message) {if (webSocket != null && isConnected) {webSocket.send(message);} else {Log.e(TAG, "Socket 未连接,无法发送消息");}}public void sendMessage(ByteString byteString) {if (webSocket != null && isConnected) {webSocket.send(byteString);} else {Log.e(TAG, "Socket 未连接,无法发送消息");}}public boolean isConnected() {return isConnected;}// ====== 独立使用的回调 ======public void setSocketListener(SocketListener listener) {this.socketListener = listener;}// ====== MVVM LiveData 接口 ======public LiveData getMessageLiveData() {return messageLiveData;}public LiveData getBinaryLiveData() {return binaryLiveData;}public LiveData getConnectionLiveData() {return connectionLiveData;}public LiveData getErrorLiveData() {return errorLiveData;}/*** WebSocket 回调*/private final WebSocketListener webSocketListener = new WebSocketListener() {@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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 给 UIpublic LiveData getMessageLiveData() {return socketManager.getMessageLiveData();}public LiveData getConnectionLiveData() {return socketManager.getConnectionLiveData();}public LiveData getErrorLiveData() {return socketManager.getErrorLiveData();}// 发送消息public void sendMessage(String msg) {socketManager.sendMessage(msg);}@Overrideprotected void onCleared() {super.onCleared();socketManager.disconnect();}
}
在 Activity/Fragment 中观察
public class SocketActivity extends AppCompatActivity {private SocketViewModel viewModel;@Overrideprotected 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() {@Overridepublic void onConnected() {Log.d("SocketTest", "连接成功");}@Overridepublic void onDisconnected(String reason) {Log.d("SocketTest", "断开连接: " + reason);}@Overridepublic void onMessage(String text) {Log.d("SocketTest", "收到消息: " + text);}@Overridepublic void onMessage(ByteString bytes) {Log.d("SocketTest", "收到二进制消息");}@Overridepublic 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 的 LiveDataprivate final MutableLiveData messageLiveData = new MutableLiveData<>();private final MutableLiveData binaryLiveData = new MutableLiveData<>();private final MutableLiveData connectionLiveData = new MutableLiveData<>();private final MutableLiveData errorLiveData = new MutableLiveData<>();// 心跳 & 超时检测 Handlerprivate final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {if (msg.what == 1) { // 心跳任务sendHeartbeat();// 发送心跳后,启动超时检测mainHandler.sendEmptyMessageDelayed(2, HEARTBEAT_TIMEOUT);} else if (msg.what == 2) { // 超时任务Log.e(TAG, "心跳超时,断开连接并尝试重连");disconnect();reconnect();}return true;}});private SocketManager() {okHttpClient = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时.build();}public static SocketManager getInstance() {if (instance == null) {synchronized (SocketManager.class) {if (instance == null) {instance = new SocketManager();}}}return instance;}public void setSocketUrl(String url) {this.socketUrl = url;}public void connect() {if (isConnected || socketUrl == null) return;Request request = new Request.Builder().url(socketUrl).build();webSocket = okHttpClient.newWebSocket(request, webSocketListener);isConnected = true;}public void disconnect() {if (webSocket != null) {webSocket.close(1000, "主动断开连接");webSocket = null;}isConnected = false;stopHeartbeat();}public void sendMessage(String message) {if (webSocket != null && isConnected) {webSocket.send(message);} else {Log.e(TAG, "Socket 未连接,无法发送消息");}}public void sendMessage(ByteString byteString) {if (webSocket != null && isConnected) {webSocket.send(byteString);} else {Log.e(TAG, "Socket 未连接,无法发送消息");}}public boolean isConnected() {return isConnected;}// ====== 独立使用的回调 ======public void setSocketListener(SocketListener listener) {this.socketListener = listener;}// ====== MVVM LiveData 接口 ======public LiveData getMessageLiveData() {return messageLiveData;}public LiveData getBinaryLiveData() {return binaryLiveData;}public LiveData getConnectionLiveData() {return connectionLiveData;}public LiveData getErrorLiveData() {return errorLiveData;}/*** 发送心跳包*/private void sendHeartbeat() {if (isConnected) {Log.d(TAG, "发送心跳包: " + HEARTBEAT_MSG);sendMessage(HEARTBEAT_MSG);}}/*** 启动心跳任务*/private void startHeartbeat() {stopHeartbeat();mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);}/*** 停止心跳任务*/private void stopHeartbeat() {mainHandler.removeMessages(1);mainHandler.removeMessages(2);}/*** 重置心跳计时器(收到任何消息时调用)*/private void resetHeartbeat() {stopHeartbeat();mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);}/*** WebSocket 回调*/private final WebSocketListener webSocketListener = new WebSocketListener() {@Overridepublic 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();}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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();}@Overridepublic 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();}@Overridepublic 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() {@Overridepublic void onConnected() {Log.d("SocketTest", "连接成功");}@Overridepublic void onDisconnected(String reason) {Log.d("SocketTest", "断开连接: " + reason);}@Overridepublic void onMessage(String text) {Log.d("SocketTest", "收到消息: " + text);}@Overridepublic void onMessage(ByteString bytes) {}@Overridepublic 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;@Overridepublic 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());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "SocketService onStartCommand");return START_STICKY; // 服务被杀死后自动重启}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic 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);}}// 点击通知回到 MainActivityIntent intent = new Intent(this, MainActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE);return new NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("Socket 连接中").setContentText("保持后台实时通信").setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pendingIntent).setPriority(NotificationCompat.PRIORITY_LOW).build();}
}
在 AndroidManifest.xml 注册 Service
启动 Service 的方式
从 Application 启动(推荐)
public class MyApplication extends Application {@Overridepublic 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 的 LiveDataprivate final MutableLiveData messageLiveData = new MutableLiveData<>();private final MutableLiveData binaryLiveData = new MutableLiveData<>();private final MutableLiveData connectionLiveData = new MutableLiveData<>();private final MutableLiveData errorLiveData = new MutableLiveData<>();// 心跳 & 超时检测 Handlerprivate final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {if (msg.what == 1) { // 心跳任务sendHeartbeat();// 发送心跳后,启动超时检测mainHandler.sendEmptyMessageDelayed(2, HEARTBEAT_TIMEOUT);} else if (msg.what == 2) { // 超时任务Log.e(TAG, "心跳超时,断开连接并尝试重连");disconnect();reconnect();}return true;}});private SocketManager() {okHttpClient = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket 不需要超时.build();}public static SocketManager getInstance() {if (instance == null) {synchronized (SocketManager.class) {if (instance == null) {instance = new SocketManager();}}}return instance;}public void setSocketUrl(String url) {this.socketUrl = url;}public void connect() {if (isConnected || socketUrl == null) return;Request request = new Request.Builder().url(socketUrl).build();webSocket = okHttpClient.newWebSocket(request, webSocketListener);isConnected = true;}public void disconnect() {if (webSocket != null) {webSocket.close(1000, "主动断开连接");webSocket = null;}isConnected = false;stopHeartbeat();}public void sendMessage(String message) {if (webSocket != null && isConnected) {webSocket.send(message);} else {Log.e(TAG, "Socket 未连接,无法发送消息");}}public void sendMessage(ByteString byteString) {if (webSocket != null && isConnected) {webSocket.send(byteString);} else {Log.e(TAG, "Socket 未连接,无法发送消息");}}public boolean isConnected() {return isConnected;}// ====== 独立使用的回调 ======public void setSocketListener(SocketListener listener) {this.socketListener = listener;}// ====== MVVM LiveData 接口 ======public LiveData getMessageLiveData() {return messageLiveData;}public LiveData getBinaryLiveData() {return binaryLiveData;}public LiveData getConnectionLiveData() {return connectionLiveData;}public LiveData getErrorLiveData() {return errorLiveData;}/*** 发送心跳包*/private void sendHeartbeat() {if (isConnected) {Log.d(TAG, "发送心跳包: " + HEARTBEAT_MSG);sendMessage(HEARTBEAT_MSG);}}/*** 启动心跳任务*/private void startHeartbeat() {stopHeartbeat();mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);}/*** 停止心跳任务*/private void stopHeartbeat() {mainHandler.removeMessages(1);mainHandler.removeMessages(2);}/*** 重置心跳计时器(收到任何消息时调用)*/private void resetHeartbeat() {stopHeartbeat();mainHandler.sendEmptyMessageDelayed(1, HEARTBEAT_INTERVAL);}/*** WebSocket 回调*/private final WebSocketListener webSocketListener = new WebSocketListener() {@Overridepublic 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();}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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();}@Overridepublic 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();}@Overridepublic 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;@Overridepublic 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());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "SocketService onStartCommand");return START_STICKY; // 服务被杀死后自动重启}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic 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);}}// 点击通知回到 MainActivityIntent intent = new Intent(this, MainActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE);return new NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("Socket 连接中").setContentText("保持后台实时通信").setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pendingIntent).setPriority(NotificationCompat.PRIORITY_LOW).build();}/*** 连接状态观察者*/private final Observer connectionObserver = isConnected -> {if (isConnected != null && isConnected) {Log.d(TAG, "Socket 连接成功(Service 观察到)");// 可更新通知内容} else {Log.d(TAG, "Socket 已断开(Service 观察到)");}};
}
启动 Service
// 在 Application 或 Activity 中启动
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {context.startForegroundService(new Intent(context, SocketService.class));
} else {context.startService(new Intent(context, SocketService.class));
}
UI 层通过 ViewModel 观察
public class SocketViewModel extends AndroidViewModel {private final SocketManager socketManager;public SocketViewModel(@NonNull Application application) {super(application);socketManager = SocketManager.getInstance();}public LiveData getMessageLiveData() {return socketManager.getMessageLiveData();}public LiveData getConnectionLiveData() {return socketManager.getConnectionLiveData();}public void sendMessage(String msg) {socketManager.sendMessage(msg);}
}
整合后的优势
✅ Service 负责 Socket 生命周期(启动 / 销毁 / 重连)✅ UI 层完全解耦,只通过 LiveData 观察数据✅ 后台保活,通过前台服务提高优先级✅ 心跳检测 保持连接稳定✅ 自动重连 机制✅ 支持独立使用(不依赖 MVVM)
✅ 这样你的长连接模块就非常完善了,可以直接在项目中使用,而且结构清晰,Service 负责后台运行,Manager 负责业务逻辑,UI 层只管展示数据。
封装6,再加上 “网络状态变化自动重连” 功能,当设备网络从无到有时,自动重新连接 Socket。SocketManager 增加一个 网络状态变化自动重连 的功能,这样当设备从无网络切换到有网络时,Socket 会自动尝试重新连接,保证长连接的稳定性。
设计思路
- 使用
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() {@Overridepublic void onAvailable(Network network) {super.onAvailable(network);Log.d(TAG, "网络已恢复,尝试重连 Socket");if (!isConnected()) {connect();}}@Overridepublic void onLost(Network network) {super.onLost(network);Log.d(TAG, "网络已断开,关闭 Socket");if (isConnected()) {disconnect();}}};}
}
/*** 注册网络监听(建议在 Application 初始化时调用)*/
public void registerNetworkListener(Context context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {NetworkUtils.registerNetworkCallback(context, networkCallback);}
}
/*** 取消网络监听*/
public void unregisterNetworkListener(Context context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {NetworkUtils.unregisterNetworkCallback(context, networkCallback);}
}
最终完整代码集合如下
完整的 Socket 长连接管理代码,支持到 Android 16(Android 4.1),包含我们之前讨论的所有功能:
- MVVM + LiveData 支持
- 自动重连(连接失败 / 断开)
- 心跳检测(定时发送 ping)
- 后台保活(前台服务)
- 网络状态变化自动重连
- 连接状态持久化(SharedPreferences)
1. 目录结构
plaintext
app/src/main/java/com/yourpackage/socket/
├── SocketManager.java // 核心管理类(单例)
├── SocketService.java // 前台服务保活
├── NetworkUtils.java // 网络工具类(判断网络状态 + 监听网络变化)
├── MyApplication.java // 全局 Application
└── viewmodel/└── SocketViewModel.java // ViewModel 示例
2. NetworkUtils.java(网络工具类)
package com.yourpackage.socket;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
/*** 网络工具类* 支持 Android 16+*/
public class NetworkUtils {/*** 判断是否有网络连接*/public static boolean isNetworkConnected(Context context) {if (context == null) return false;ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);if (cm == null) return false;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {Network network = cm.getActiveNetwork();if (network == null) return false;NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);return capabilities != null && (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET));} else {NetworkInfo info = cm.getActiveNetworkInfo();return info != null && info.isConnected();}}/*** 注册网络变化监听(Android 7.0+)*/public static void registerNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);if (cm != null) {cm.registerDefaultNetworkCallback(callback);}}}/*** 取消网络变化监听*/public static void unregisterNetworkCallback(Context context, ConnectivityManager.NetworkCallback callback) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);if (cm != null) {cm.unregisterNetworkCallback(callback);}}}
}
3. SocketManager.java(核心管理类)
package com.yourpackage.socket;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Network;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/*** Socket 长连接管理类(单例模式)* 功能:* 1. 自动重连* 2. 心跳检测* 3. 网络状态监听* 4. 连接状态持久化* 5. LiveData 数据分发*/
public class SocketManager {private static final String TAG = "SocketManager";private static final String PREF_NAME = "socket_prefs";private static final String KEY_CONNECTED = "is_connected";private static final String KEY_SOCKET_URL = "socket_url";private static SocketManager instance;private OkHttpClient okHttpClient;private WebSocket webSocket;private String socketUrl;private boolean isConnected = false;private MutableLiveData connectionLiveData = new MutableLiveData<>();private MutableLiveData messageLiveData = new MutableLiveData<>();private MutableLiveData errorLiveData = new MutableLiveData<>();private Handler mainHandler = new Handler(Looper.getMainLooper());private Runnable heartbeatRunnable;private static final long HEARTBEAT_INTERVAL = 10 * 1000; // 10秒private ConnectivityManager.NetworkCallback networkCallback;private SocketManager() {okHttpClient = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build();initNetworkCallback();restoreConnectionState();}public static synchronized SocketManager getInstance() {if (instance == null) {instance = new SocketManager();}return instance;}public void setSocketUrl(String url) {this.socketUrl = url;}public LiveData getConnectionLiveData() {return connectionLiveData;}public LiveData getMessageLiveData() {return messageLiveData;}public LiveData getErrorLiveData() {return errorLiveData;}public boolean isConnected() {return isConnected;}/*** 连接 WebSocket*/public void connect() {if (isConnected || socketUrl == null) return;Request request = new Request.Builder().url(socketUrl).build();webSocket = okHttpClient.newWebSocket(request, webSocketListener);updateConnectionState(true);}/*** 断开连接*/public void disconnect() {if (webSocket != null) {webSocket.close(1000, "主动断开连接");webSocket = null;}updateConnectionState(false);stopHeartbeat();}/*** 发送消息*/public void sendMessage(String text) {if (webSocket != null) {webSocket.send(text);}}/*** 初始化网络监听*/private void initNetworkCallback() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {networkCallback = new ConnectivityManager.NetworkCallback() {@Overridepublic void onAvailable(Network network) {super.onAvailable(network);if (!isConnected()) {connect();}}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onOpen(WebSocket webSocket, Response response) {super.onOpen(webSocket, response);updateConnectionState(true);startHeartbeat();}@Overridepublic void onMessage(WebSocket webSocket, String text) {super.onMessage(webSocket, text);messageLiveData.postValue(text);}@Overridepublic void onMessage(WebSocket webSocket, ByteString bytes) {super.onMessage(webSocket, bytes);// 处理二进制消息}@Overridepublic void onClosed(WebSocket webSocket, int code, String reason) {super.onClosed(webSocket, code, reason);updateConnectionState(false);stopHeartbeat();}@Overridepublic 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";@Overridepublic void onCreate() {super.onCreate();startForeground(NOTIFICATION_ID, createNotification());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 确保Socket连接if (!SocketManager.getInstance().isConnected()) {SocketManager.getInstance().connect();}return START_STICKY;}@Overridepublic 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;}@Overridepublic void onCreate() {super.onCreate();context = this;// 初始化SocketManagerSocketManager.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);}}@Overridepublic void onTerminate() {super.onTerminate();SocketManager.getInstance().unregisterNetworkListener(this);}
}
6. SocketViewModel.java(ViewModel 示例)
java
运行
package com.yourpackage.socket.viewmodel;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.yourpackage.socket.SocketManager;
/*** Socket ViewModel*/
public class SocketViewModel extends AndroidViewModel {private SocketManager socketManager;public SocketViewModel(@NonNull Application application) {super(application);socketManager = SocketManager.getInstance();}public LiveData getConnectionState() {return socketManager.getConnectionLiveData();}public LiveData getMessages() {return socketManager.getMessageLiveData();}public LiveData getErrors() {return socketManager.getErrorLiveData();}public void sendMessage(String message) {socketManager.sendMessage(message);}
}
7. 权限配置(AndroidManifest.xml)
xml
✅ 这样你就有了一套完整的 Socket 长连接解决方案:
- 支持 Android 16+
- 自动重连
- 心跳检测
- 网络状态监听
- 连接状态持久化
- MVVM + LiveData 架构
- 前台服务保活
