Android 利用 WebSocket 与服务器互相推送消息

围巾🧣 2024年07月05日 151次浏览

简介

WebSocket 是一种常见的用于在 Web 服务器和 Android 客户端之间进行实时通信的协议。它提供了全双工通信通道,允许服务器主动向客户端推送消息,而无需客户端频繁轮询服务器。

注意事项:

  1. 安全性:在生产环境中,建议使用 wss(WebSocket Secure)而不是 ws,以确保数据传输的安全性。
  2. 重连机制:在实际应用中,网络状况可能会导致 WebSocket 连接断开,因此需要实现重连守护机制。
  3. 心跳机制:为了检测连接的有效性,可以实现心跳机制,定期发送心跳包。

服务端

在使用 Spring Boot 构建 Java WebSocket 服务器时,可以利用 Spring Boot 的 WebSocket 支持来实现。以下是具体步骤:

1. 添加依赖

在你的 pom.xml 文件中添加 WebSocket 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 配置 WebSocket

创建一个配置类,来注册 WebSocket 端点和处理器:

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MyWebSocketHandler(), "/websocket")
                .setAllowedOrigins("*");
    }
}

3. 创建 WebSocket 处理器

创建一个类来处理 WebSocket 消息:

@Service
public class MyWebSocketHandler extends TextWebSocketHandler {

    private static List<WebSocketSession> sessions = new ArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("add: session");
        sessions.add(session);
        session.sendMessage(new TextMessage("Connection established"));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("Received message: " + message.getPayload());
        for (WebSocketSession s : sessions) {
            if (s.isOpen()) {
                s.sendMessage(message);
            }
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("remove: session");
        sessions.remove(session);
    }

    public void broadcast(String message) throws IOException {
        for (WebSocketSession session : sessions) {
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(message));
            }
        }
    }
}

4. 在需要时推送消息

你可以在 Spring Boot 应用中的任何地方调用 MyWebSocketHandlerbroadcast 方法来向所有连接的客户端推送消息。例如,在一个 REST 控制器中:

@RestController
@Api(tags = "WebSocketController")
@RequestMapping("/websocket")
public class WebSocketController {

    @Autowired
    private MyWebSocketHandler webSocketHandler;

    @GetMapping("/send")
    public String sendMessage(@RequestParam String message) throws IOException {
        webSocketHandler.broadcast(message);
        return "Message sent!!";
    }
}

客户端实现

使用okhttp

在 Android 端可以使用前面提到的 OkHttp 库实现 WebSocket 客户端。

public class WebSocketClient extends WebSocketListener {

    private OkHttpClient client;
    private OnMessageListener listener;


    public void start() {
        client = new OkHttpClient();
        Request request = new Request.Builder().url("ws://192.168.222.81:20000/websocket").build();
        WebSocket ws = client.newWebSocket(request, this);
        // 这行代码在不需要更多HTTP请求的情况下关闭调度器的执行器服务,如果后续只用 websocket 就可以调用
        client.dispatcher().executorService().shutdown();
    }

    @Override
    public void onOpen(WebSocket webSocket, okhttp3.Response response) {
        webSocket.send("Hello, it's WebSocket Client!");
    }

    @Override
    public void onMessage(WebSocket webSocket, String text) {
        System.out.println("Receiving : " + text);
        new Handler(Looper.getMainLooper()).post(() -> {
            if (listener != null) {
                listener.onMessage(text);
            }
        });
    }

    @Override
    public void onMessage(WebSocket webSocket, ByteString bytes) {
        System.out.println("Receiving bytes : " + bytes.hex());
    }

    @Override
    public void onClosing(WebSocket webSocket, int code, String reason) {
        webSocket.close(1000, null);
        System.out.println("WebSocket is closing : " + code + " cause: " + reason);
    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, okhttp3.Response response) {
        t.printStackTrace();
    }

    public void setOnMessageListener(OnMessageListener listener) {
        this.listener = listener;
    }

    public interface OnMessageListener {
        void onMessage(String text);
    }
}

在 Android 应用中调用 start() 方法来启动 WebSocket 连接:

val client = WebSocketClient()
client.start()

通过以上步骤,就可以实现 Spring Boot 服务端和 Android 客户端之间的 WebSocket 实时通信。

网络配置

在 Android 9.0(API 级别 28)及更高版本中,默认情况下,应用程序不允许进行纯文本(未加密)的 HTTP 通信。为了允许使用 WebSocket 进行未加密的 ws:// 通信,需要在 Android 应用的 network_security_config.xml 文件中进行配置,并在 AndroidManifest.xml 中引用这个配置。

1. network_security_config.xml

res/xml/ 目录下创建一个 network_security_config.xml 文件(如果 xml 目录不存在,则需要先创建它),并添加以下内容:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">192.168.222.81</domain>
    </domain-config>
</network-security-config>

这个配置文件允许与指定的 IP 地址进行纯文本通信。

2. 更新 AndroidManifest.xml

在你的 AndroidManifest.xml 文件中的 <application> 标签内添加对 network_security_config.xml 的引用:

<application
    android:networkSecurityConfig="@xml/network_security_config"
    ... >
    ...
</application>