Android+Spring Boot聊天应用中WebSocket onMessage方法未触发求助
问题:Android OkHttp WebSocket无法接收Spring Boot后端返回的STOMP消息
前端(Android OkHttp)代码
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button connectBtn=findViewById(R.id.connectBtn); connectBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url("ws://localhost:8080/ws").build(); WebSocketListener listener=new WebSocketListener() { @Override public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { super.onClosed(webSocket, code, reason); output("Closed: "+reason); } @Override public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) { super.onClosing(webSocket, code, reason); output("Closing: "+reason); } @Override public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) { super.onFailure(webSocket, t, response); output("Failed: "+ t); } @Override public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) { super.onMessage(webSocket, text); output("Received: "+text); } @Override public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) { super.onMessage(webSocket, bytes); output("Received bytes: "+bytes.hex()); } @Override public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { super.onOpen(webSocket, response); Message message=new Message("Hello"); Gson gson=new Gson(); String jsonMessage=gson.toJson(message); output("connected: "+response); webSocket.send("SUBSCRIBE\n" + "destination:/topic/public\n" + "id:sub-01\n" + "\n" + "\0"); webSocket.send("SEND\n" + "destination:/app/message\n" + "Content-Type: application/json\n"+ "\n" + jsonMessage + "\0"); } }; WebSocket webSocket= client.newWebSocket(request,listener); } }); } public void output(String s){ runOnUiThread(new Runnable() { @Override public void run() { System.out.println(s); } }); } }
后端(Spring Boot)代码
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws"); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.setApplicationDestinationPrefixes("/app"); registry.enableSimpleBroker("/topic"); } } @Controller public class ChatController { @MessageMapping("/message") @SendTo("/topic/public") public Message sendMessage(Message message){ System.out.println(message); return message; } }
问题描述
使用Android(OkHttp)和Spring Boot开发聊天应用,已成功建立WebSocket连接,消息"Hello"可正常发送至后端并打印;后端sendMessage方法已返回Message对象,但WebSocket监听器的onMessage方法始终未被调用。
解决方案
1. 核心问题:STOMP协议与原生WebSocket的差异
你用Spring Boot配置的是STOMP协议(基于WebSocket的上层应用协议),但OkHttp的WebSocket只是底层的字节流传输工具,不会自动解析STOMP的帧格式。后端返回的是完整的STOMP消息帧,而非单纯的JSON数据,你的前端代码没有正确处理这种格式,导致看似没收到消息(实际可能收到了但没识别)。
2. 先确认是否真的收到消息
修改onMessage方法,完整打印收到的原始文本:
@Override public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) { super.onMessage(webSocket, text); // 完整打印所有收到的内容,包括STOMP帧的头部和分隔符 output("Raw received content:\n" + text); }
如果收到类似以下内容,说明消息已到达,只是需要解析:
MESSAGE destination:/topic/public content-type:application/json content-length:13 {"content":"Hello"} \0
3. 两种处理方式
方式一:使用STOMP客户端库(推荐)
直接用现成的STOMP客户端库处理协议解析,避免手动处理帧格式。比如stomp-android:
- 添加Gradle依赖:
implementation 'com.github.NaikSoftware:StompProtocolAndroid:2.0.7' - 示例代码:
StompClient stompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, "ws://localhost:8080/ws"); // 订阅主题 stompClient.topic("/topic/public").subscribe(topicMessage -> { // 直接拿到解析后的JSON body String jsonBody = topicMessage.getPayload(); Message message = new Gson().fromJson(jsonBody, Message.class); output("Received message: " + message.getContent()); }); // 连接并发送消息 stompClient.connect(); stompClient.send("/app/message", new Gson().toJson(new Message("Hello"))).subscribe();
方式二:手动解析STOMP帧(不推荐)
如果坚持用OkHttp原生处理,需要自己拆分STOMP帧的结构:
STOMP帧格式为:COMMAND\nHEADERS\n\nBODY\0
可以按以下步骤解析:
@Override public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) { super.onMessage(webSocket, text); // 去掉末尾的\0分隔符 String frame = text.replace("\0", ""); // 分割帧的各个部分 String[] parts = frame.split("\n\n", 2); if (parts.length == 2) { // parts[0]是COMMAND和HEADERS,parts[1]是BODY(即后端返回的JSON) String jsonBody = parts[1].trim(); Message message = new Gson().fromJson(jsonBody, Message.class); output("Received parsed message: " + message.getContent()); } }
4. 额外注意点
- 确保Android设备能访问后端的
ws://localhost:8080/ws:如果是模拟器,localhost对应模拟器的10.0.2.2,所以URL要改成ws://10.0.2.2:8080/ws。 - 检查后端是否正确配置了跨域:如果Android应用和后端不在同一域,需要在
WebSocketConfig中添加跨域支持:@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").setAllowedOriginPatterns("*"); }
内容的提问来源于stack exchange,提问作者Awsaf Tasusif 2132066642
相关产品推荐
相关产品推荐

