Android 中实现稳定的 Socket 长连接:自动重连、心跳机制与前台服务保活
在实时通讯、物联网、无人机遥控、设备上传、IM(即时通讯)等场景中,Socket 长连接是最关键的通信方式之一。
但在 Android 上稳定维护 Socket 长连接并不是一件容易的事,尤其是:
App 切后台后连接断开
网络切换(4G/WiFi)导致连接失败
服务被系统回收
心跳不稳定导致服务端主动断开
需要自动重连、保活
本篇文章从实践角度讲解:
如何在 Android 中构建一个 稳定、可重连、可保活 的 Socket 通信模块。
1. 整体架构设计
推荐使用 前台服务 + 单独 Socket 线程 + RxJava 消息分发 的组合:
SocketService(前台服务)
│
├── SocketClient(核心连接模块)
│ ├── connect()
│ ├── disconnect()
│ ├── send()
│ ├── listenThread(接收消息)
│ ├── heartBeatThread(心跳包)
│ ├── autoReconnect()
│
└── DataBus(RxJava 发布订阅)
优点:
服务不容易被杀死 → 前台服务
Socket 独立线程运行 → 不阻塞 UI
RxJava → 方便上层 Activity 订阅数据
独立心跳和自动重连机制 → 稳定持续通信
2. 创建前台服务 SocketService
public class SocketService extends Service {
private SocketClient socketClient;
@Override
public void onCreate() {
super.onCreate();
startForegroundService();
socketClient = new SocketClient("192.168.1.100", 9000);
socketClient.connect();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY; // 保活关键
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void startForegroundService() {
NotificationChannel channel = new NotificationChannel(
"socket_channel", "Socket Service", NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "socket_channel")
.setContentTitle("Socket 正在运行")
.setSmallIcon(R.mipmap.ic_launcher)
.build();
startForeground(1, notification);
}
}
关键点:
START_STICKY 让服务被系统杀死后自动重启
前台服务可以在后台长期稳定运行
3. SocketClient:核心连接模块
public class SocketClient {
private String host;
private int port;
private Socket socket;
private boolean isRunning = false;
public SocketClient(String host, int port) {
this.host = host;
this.port = port;
}
public void connect() {
new Thread(() -> {
try {
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), 5000);
isRunning = true;
startReadThread();
startHeartBeat();
} catch (Exception e) {
autoReconnect();
}
}).start();
}
4. 自动重连机制
private void autoReconnect() {
new Thread(() -> {
while (!isRunning) {
try {
Thread.sleep(3000);
Log.d("Socket", "正在重连...");
connect();
} catch (InterruptedException ignored) {}
}
}).start();
}
特点:
服务端断开 / 网络切换时自动重连
防止频繁重连 → 每 3 秒尝试一次
5. 心跳包(Heartbeat)
为了防止服务器认为客户端掉线,需要定时发送心跳:
private void startHeartBeat() {
new Thread(() -> {
while (isRunning) {
try {
send("HEARTBEAT".getBytes());
Thread.sleep(5000);
} catch (Exception e) {
isRunning = false;
autoReconnect();
}
}
}).start();
}
你也可以使用协议规定的心跳帧,比如 0x55 AA 00 01 00.
6. 接收消息线程
private void startReadThread() {
new Thread(() -> {
try {
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
while (isRunning) {
int len = in.read(buffer);
if (len > 0) {
byte[] data = Arrays.copyOf(buffer, len);
DataBus.post(data);
}
}
} catch (Exception e) {
isRunning = false;
autoReconnect();
}
}).start();
}
收到的消息通过 DataBus 分发到 UI。
7. RxJava 消息总线
public class DataBus {
private static final PublishSubject<byte[]> bus = PublishSubject.create();
public static void post(byte[] data) {
bus.onNext(data);
}
public static Observable<byte[]> toObservable() {
return bus;
}
}
在 Activity 中监听:
DataBus.toObservable()
.subscribe(bytes -> {
Log.d("SocketData", ByteUtils.toHex(bytes));
});
8. 发送数据
public void send(byte[] data) {
new Thread(() -> {
try {
OutputStream out = socket.getOutputStream();
out.write(data);
out.flush();
} catch (Exception e) {
isRunning = false;
autoReconnect();
}
}).start();
}
9. Activity 中启动服务并通信
Intent intent = new Intent(this, SocketService.class);
startForegroundService(intent);
findViewById(R.id.btn_send).setOnClickListener(v -> {
socketClient.send("Hello".getBytes());
});
10. 总结:这是一套可上线的 Socket 长连接方案
功能 状态
前台服务保活 ✔
自动重连 ✔
网络切换重连 ✔
心跳保持在线 ✔
独立读写线程 ✔
RxJava 分发消息 ✔
UI 可随时订阅 ✔
以下是可用工具类:
package com.htnova.common.socket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SocketClient {
private String host;
private int port;
private volatile boolean isConnected = false;
private Socket socket;
private BufferedInputStream inputStream;
private BufferedOutputStream outputStream;
private ExecutorService socketThread = Executors.newSingleThreadExecutor();
private ExecutorService sendThread = Executors.newSingleThreadExecutor();
private SocketCallback callback;
private boolean isManualClose = false;
private int reconnectDelay = 3000; //自动重连间隔
private int heartbeatInterval = 5000; //心跳间隔(毫秒)
private byte[] heartbeatData = "HEARTBEAT".getBytes();
public interface SocketCallback {
void onConnected();
void onDisconnected();
void onMessage(byte[] msg);
void onError(Exception e);
}
public SocketClient(String host, int port, SocketCallback callback) {
this.host = host;
this.port = port;
this.callback = callback;
}
/**
* 开始连接
*/
public void connect() {
isManualClose = false;
socketThread.execute(() -> startConnect());
}
private void startConnect() {
try {
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), 5000);
socket.setKeepAlive(true);
inputStream = new BufferedInputStream(socket.getInputStream());
outputStream = new BufferedOutputStream(socket.getOutputStream());
isConnected = true;
if (callback != null) callback.onConnected();
startReadLoop();
startHeartBeatLoop();
} catch (Exception e) {
isConnected = false;
if (callback != null) callback.onError(e);
autoReconnect();
}
}
/**
* 消息读取循环
*/
private void startReadLoop() {
socketThread.execute(() -> {
byte[] buffer = new byte[1024];
int len;
try {
while (isConnected && (len = inputStream.read(buffer)) != -1) {
byte[] data = new byte[len];
System.arraycopy(buffer, 0, data, 0, len);
if (callback != null) callback.onMessage(data);
}
} catch (Exception e) {
if (!isManualClose) {
if (callback != null) callback.onError(e);
autoReconnect();
}
} finally {
close();
}
});
}
/**
* 发送心跳包
*/
private void startHeartBeatLoop() {
socketThread.execute(() -> {
while (isConnected) {
try {
Thread.sleep(heartbeatInterval);
send(heartbeatData);
} catch (Exception ignored) { }
}
});
}
/**
* 自动重连
*/
private void autoReconnect() {
if (isManualClose) return;
isConnected = false;
try {
Thread.sleep(reconnectDelay);
} catch (InterruptedException ignored) {}
connect();
}
/**
* 发送数据
*/
public void send(byte[] data) {
sendThread.execute(() -> {
try {
if (isConnected && outputStream != null) {
outputStream.write(data);
outputStream.flush();
}
} catch (Exception e) {
if (callback != null) callback.onError(e);
}
});
}
/**
* 关闭连接
*/
public void close() {
isManualClose = true;
isConnected = false;
try {
if (socket != null) socket.close();
if (inputStream != null) inputStream.close();
if (outputStream != null) outputStream.close();
} catch (IOException ignored) {}
if (callback != null) callback.onDisconnected();
}
/** 设置心跳包 */
public void setHeartbeat(byte[] heartbeat, int intervalMs) {
this.heartbeatData = heartbeat;
this.heartbeatInterval = intervalMs;
}
/** 设置自动重连间隔 */
public void setReconnectDelay(int delayMs) {
this.reconnectDelay = delayMs;
}
}
如何使用?
1. 初始化
SocketClient socketClient = new SocketClient("192.168.1.100", 9000, new SocketClient.SocketCallback() {
@Override
public void onConnected() {
Log.d("Socket", "已连接");
}
@Override
public void onDisconnected() {
Log.d("Socket", "已断开");
}
@Override
public void onMessage(byte[] msg) {
Log.d("Socket", "收到消息:" + new String(msg));
}
@Override
public void onError(Exception e) {
Log.e("Socket", "错误:" + e.getMessage());
}
});
2. 连接服务器
socketClient.connect();
3. 发送数据
socketClient.send("Hello Server".getBytes());
4. 设置心跳包
socketClient.setHeartbeat("PING".getBytes(), 3000);
5. 关闭连接
socketClient.close();
安卓socket
安卓理事人2025-11-29 2:03
相关推荐
安卓理事人7 小时前
安卓LinkedBlockingQueue消息队列万能的小裴同学8 小时前
Android M3U8视频播放器q***57748 小时前
MySql的慢查询(慢日志)JavaNoober9 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档城东米粉儿9 小时前
关于ObjectAnimatorzhangphil10 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU火柴就是我11 小时前
从头写一个自己的applichong95112 小时前
XLog debug 开启打印日志,release 关闭打印日志用户693717500138413 小时前
14.Kotlin 类:类的形态(一):抽象类 (Abstract Class)