You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端WebSocket对接SpringBoot聊天服务问题求助

Solution for Android Client Connecting to Spring Boot STOMP Chat Service

Let's tackle your two problems one by one, starting with the connection issue since that's blocking you first.

1. Fixing the Heroku Connection Failure

The error you're seeing (OpeningHandshakeException: The status code of the opening handshake response is not '101 Switching Protocols') happens for two key reasons:

  • Your Spring Boot service uses a SockJS endpoint, but your Android code is using a raw WebSocket client. SockJS has a specific handshake flow (it starts with an HTTP 200 check before upgrading to WebSocket) that raw clients don't handle.
  • Heroku enforces HTTPS by default, so you need to use wss:// (WebSocket Secure) instead of ws:// for your endpoint URL—plain HTTP connections may be blocked or redirected.

Fix Steps:

First, use a STOMP + SockJS compatible Android client. A popular choice is the stomp-websocket-android library.

Add Dependency

Add this to your app-level build.gradle (Module level):

implementation 'com.github.gdiazs:stomp-websocket-android:1.6.0'

Update Connection Code

Replace your ServerConnection class with code that uses the STOMP client, handles SockJS, and uses the correct wss:// URL:

import android.os.AsyncTask;
import android.util.Log;
import com.github.gdiazs.stompandroid.StompClient;
import com.github.gdiazs.stompandroid.dto.LifecycleEvent;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

class ServerConnection extends AsyncTask<Void, Void, Void> {
    // Use wss:// for Heroku's HTTPS environment
    private static final String CHAT_SERVER = "wss://chat-vu.herokuapp.com/chat-messaging";
    private StompClient stompClient;

    @Override
    protected Void doInBackground(Void... voids) {
        // Initialize STOMP client with SockJS support
        stompClient = new StompClient(CHAT_SERVER, true); // true = enable SockJS

        // Connect and handle lifecycle events
        stompClient.connect()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(lifecycleEvent -> {
                    switch (lifecycleEvent.getType()) {
                        case OPENED:
                            Log.d("myLog", "Connected to server successfully!");
                            // Subscribe to your destinations once connected
                            subscribeToMessages();
                            // Send a test echo message
                            sendEchoMessage("Hi from Android!");
                            break;
                        case ERROR:
                            Log.e("myLog", "Connection error: ", lifecycleEvent.getException());
                            break;
                        case CLOSED:
                            Log.d("myLog", "Connection closed");
                            break;
                    }
                });

        return null;
    }

    // Subscribe to different message destinations
    private void subscribeToMessages() {
        // Subscribe to private messages (/user/queue/reply)
        stompClient.topic("/user/queue/reply")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(topicMessage -> {
                    String messageBody = topicMessage.getPayload();
                    Log.d("myLog", "Received private message: " + messageBody);
                    // Parse JSON and handle the message here
                });

        // Subscribe to echo test replies (/queue/echo-reply)
        stompClient.topic("/queue/echo-reply")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(topicMessage -> {
                    String echoMessage = topicMessage.getPayload();
                    Log.d("myLog", "Received echo reply: " + echoMessage);
                });
    }

    // Send message to /app/echo test endpoint
    private void sendEchoMessage(String text) {
        stompClient.send("/app/echo", text)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(() -> Log.d("myLog", "Echo message sent: " + text),
                        error -> Log.e("myLog", "Failed to send echo message: ", error));
    }

    // Send message to /app/message-flow (for real chat)
    private void sendChatMessage(String messageJson) {
        stompClient.send("/app/message-flow", messageJson)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(() -> Log.d("myLog", "Chat message sent"),
                        error -> Log.e("myLog", "Failed to send chat message: ", error));
    }

    // Remember to disconnect when done (e.g., in onDestroy)
    public void disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
    }
}

Additional Setup

  • Add internet permission to your AndroidManifest.xml:
    <uses-permission android:name="android.permission.INTERNET" />
    
  • For Android 9+, since we're using wss:// (encrypted), you don't need extra network security config. If you ever use plain HTTP for testing, you'd need to add a network security policy to allow cleartext traffic.

2. Using Different URLs (STOMP Destinations) on Android

This works exactly like your web client—you just need to use the same destination paths with the STOMP client:

  • Sending messages: Use stompClient.send(destination, payload) where the destination matches your Spring Boot @MessageMapping paths, prefixed with /app (e.g., /app/echo, /app/message-flow).
  • Receiving messages: Use stompClient.topic(subscriptionPath) to subscribe to the queues your server sends to (e.g., /user/queue/reply for private messages, /queue/echo-reply for test echoes).

The key thing to remember is that STOMP uses destinations (logical paths) instead of raw WebSocket URLs—your client connects once to the SockJS endpoint, then sends/receives messages over that single connection using different destinations.

Final Notes

  • Make sure your Heroku service is running and the /chat-messaging SockJS endpoint is accessible.
  • When sending the Message object to /app/message-flow, serialize it to JSON just like your web client does (using userJson).
  • Always handle connection lifecycle events (error, closed) to retry connections if needed.

内容的提问来源于stack exchange,提问作者Victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:17:28