安卓socket

复制代码
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();
相关推荐
_李小白2 小时前
【Android FrameWork】延伸阅读:SurfaceFlinger线程
android
csdn12259873363 小时前
JetPack Compose 入门先搞清楚
android·compose·jetpack
liang_jy4 小时前
Android LaunchMode
android·面试
阿里云云原生5 小时前
Android App 崩溃排查实战:如何利用 RUM 完整数据与符号化技术定位问题?
android·阿里云·云原生·rum
过期动态5 小时前
JDBC高级篇:优化、封装与事务全流程指南
android·java·开发语言·数据库·python·mysql
没有了遇见7 小时前
Android 音乐播放器之MotionLayout实现View流畅变换
android
TheNextByte18 小时前
在 PC 和Android之间同步音乐的 4 种方法
android
君莫啸ོ8 小时前
Android基础-Activity属性 android:configChanges
android
TimeFine9 小时前
Android AI解放生产力(七):更丰富的AI运用前瞻
android
保持低旋律节奏9 小时前
linux——进程状态
android·linux·php