NaikSoftware / StompProtocolAndroid

STOMP protocol via WebSocket for Android
MIT License
591 stars 265 forks source link

One STOMP "MESSAGE", many calls of topic message's handler #118

Open gemann opened 6 years ago

gemann commented 6 years ago

After N unsuccessful attempts to connect and one successful attempt, library replies with N+1 messages - in this case there should only be one. It works fine when no problems with connection occur.

I used this as backend: https://spring.io/guides/gs/messaging-stomp-websocket/

Logs from an Android device:

D/StompManager: Connecting... D/StompManager: Connecting in other thread. D/StompClient: Connect D/AbstractConnectionProvider: Emit lifecycle event: ERROR D/StompManager: ################## ERROR! Trying to connect again... D/StompClient: Socket closed with error D/AbstractConnectionProvider: Emit lifecycle event: CLOSED D/StompClient: Socket closed I/Adreno-EGL: : EGL 1.4 QUALCOMM build: () OpenGL ES Shader Compiler Version: E031.24.00.07 Build Date: 04/07/14 Mon Local Branch: au011 Remote Branch: Local Patches: Reconstruct Branch: D/OpenGLRenderer: Enabling debug mode 0 I/ActivityManager: Timeline: Activity_idle id: android.os.BinderProxy@44804f48 time:354941899 D/StompManager: Connecting... D/StompManager: ################## OFFLINE! D/StompManager: Connecting in other thread. D/StompClient: Connect D/AbstractConnectionProvider: Emit lifecycle event: ERROR D/StompManager: ################## ERROR! Trying to connect again... D/StompClient: Socket closed with error D/StompClient: Socket closed with error D/AbstractConnectionProvider: Emit lifecycle event: CLOSED D/StompClient: Socket closed D/StompClient: Socket closed D/StompManager: Connecting... D/StompManager: ################## OFFLINE! D/StompManager: Connecting in other thread. D/StompClient: Connect D/AbstractConnectionProvider: Emit lifecycle event: ERROR D/StompManager: ################## ERROR! Trying to connect again... D/StompClient: Socket closed with error D/StompClient: Socket closed with error D/StompClient: Socket closed with error D/AbstractConnectionProvider: Emit lifecycle event: CLOSED D/StompClient: Socket closed D/StompClient: Socket closed D/StompClient: Socket closed D/StompManager: Connecting... D/StompManager: ################## OFFLINE! D/StompManager: Connecting in other thread. D/StompClient: Connect W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;) W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;) I/dalvikvm: Could not find method java.nio.file.Files.newOutputStream, referenced from method okio.Okio.sink W/dalvikvm: VFY: unable to resolve static method 26725: Ljava/nio/file/Files;.newOutputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/OutputStream; D/dalvikvm: VFY: replacing opcode 0x71 at 0x000a W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;) W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;) I/dalvikvm: Could not find method java.nio.file.Files.newInputStream, referenced from method okio.Okio.source W/dalvikvm: VFY: unable to resolve static method 26724: Ljava/nio/file/Files;.newInputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream; D/dalvikvm: VFY: replacing opcode 0x71 at 0x000a D/AbstractConnectionProvider: Emit lifecycle event: OPENED D/StompManager: ################## ONLINE! D/AbstractConnectionProvider: Send STOMP message: CONNECT version:1.1,1.0 heart-beat:0,0 �� D/AbstractConnectionProvider: Send STOMP message: CONNECT version:1.1,1.0 heart-beat:0,0 �� D/AbstractConnectionProvider: Send STOMP message: CONNECT version:1.1,1.0 heart-beat:0,0 �� D/AbstractConnectionProvider: Send STOMP message: CONNECT version:1.1,1.0 heart-beat:0,0 �� D/AbstractConnectionProvider: Emit STOMP message: CONNECTED heart-beat:0,0 �� D/AbstractConnectionProvider: Send STOMP message: SUBSCRIBE id:a0621439-4e0a-4f0a-89f4-4f838a75ea46 destination:/topic/greetings ack:auto �� D/AbstractConnectionProvider: Emit STOMP message: CONNECTED heart-beat:0,0 �� D/AbstractConnectionProvider: Emit STOMP message: CONNECTED heart-beat:0,0 �� D/AbstractConnectionProvider: Emit STOMP message: CONNECTED heart-beat:0,0 �� I/ActivityManager: Timeline: Activity_idle id: android.os.BinderProxy@44804f48 time:354980497 D/StompManager: Sending message D/AbstractConnectionProvider: Send STOMP message: SEND destination:/app/hello {"name":"Someone"}�� D/AbstractConnectionProvider: Emit STOMP message: MESSAGE destination:/topic/greetings content-type:application/json;charset=UTF-8 subscription:a0621439-4e0a-4f0a-89f4-4f838a75ea46 message-id:0-0 content-length:29 {"content":"Hello, Someone!"}�� D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/StompManager: Sending message D/AbstractConnectionProvider: Send STOMP message: SEND destination:/app/hello {"name":"Someone"}�� D/AbstractConnectionProvider: Emit STOMP message: MESSAGE destination:/topic/greetings content-type:application/json;charset=UTF-8 subscription:a0621439-4e0a-4f0a-89f4-4f838a75ea46 message-id:0-1 content-length:29 {"content":"Hello, Someone!"}�� D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"} D/MainActivity: Greeting incoming: {"content":"Hello, Someone!"}

StompManager:

package com.example.stompapp;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;

import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import ua.naiksoftware.stomp.LifecycleEvent;
import ua.naiksoftware.stomp.Stomp;
import ua.naiksoftware.stomp.client.StompClient;
import ua.naiksoftware.stomp.client.StompMessage;

public class StompManager {

    public static final String LOG_TAG = "StompManager";

    private volatile HandlerThread handlerThread;
    private ServiceHandler serviceHandler;

    private StompClient stompClient;

    public StompManager(String url) {
        handlerThread = new HandlerThread("ConnectionService.HandlerThread");
        handlerThread.start();
        serviceHandler = new ServiceHandler(handlerThread.getLooper());

        stompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, url);

        stompClient.lifecycle()
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.computation())
                .subscribe(this::handleConnectionLifecycle);
    }

    public void connect() {
        Log.d(LOG_TAG, "Connecting...");
        serviceHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.d(LOG_TAG, "Connecting in other thread.");
                stompClient.connect();
            }
        });
    }

    public void disconnect() {
        stompClient.disconnect();
    }

    public void send(String mapping, String message) {
        Log.d(LOG_TAG, "Sending message");
        stompClient.send(mapping, message).subscribe();
    }

    public void subscribeTopic(String topic, Consumer<StompMessage> handler) {
        stompClient.topic(topic)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.computation())
                .subscribe(handler);
    }

    private void handleConnectionLifecycle(LifecycleEvent event) {
        switch (event.getType()) {
            case OPENED:
                Log.d(LOG_TAG,"################## ONLINE!");
                break;
            case ERROR:
                Log.d(LOG_TAG, "################## ERROR! Trying to connect again...");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    Log.d(LOG_TAG, "Thread.sleep() exception.");
                }
                connect();
                break;
            case CLOSED:
                Log.d(LOG_TAG, "################## OFFLINE!");
                break;
        }

    }

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
    }   
}

MainActivity:


package com.example.stompapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class MainActivity extends AppCompatActivity {

    private static final String LOG_TAG = "MainActivity";

    private StompManager stompManager;
    private Gson gson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        stompManager = new StompManager("ws://10.0.1.2:8080/gs-guide-websocket/websocket");
        stompManager.connect();

        stompManager.subscribeTopic("/topic/greetings", stompMessage -> Log.d(LOG_TAG, "Greeting incoming: " + stompMessage.getPayload()));

        gson = new GsonBuilder().create();

    }

    @Override
    protected void onDestroy() {
        stompManager.disconnect();
        stompManager = null;
        super.onDestroy();
    }

    public void onSendButtonClick(View view) {
        stompManager.send("/app/hello", gson.toJson(new HelloMessage("Someone")));
    }

}
forresthopkinsa commented 6 years ago

The errors you're encountering are not "fatal" in an Rx sense, so the subscriptions are still active. You need to hold onto the Subscription objects and unsubscribe them manually, or else you'll get memory leaks like this.

testGumar commented 5 years ago

Use this library its working fine, replace server url and port with your custom server url and your port. https://github.com/SayyedUmar/Stomp-Android-Client