HiveMQ

Attempt a reconnect, while automatic reconnect is scheduled

Hello everybody,

I m currently running the hiveMQ client on android. I need to be able to attempt a connect while there is an autoreconnect scheduled or make this autoreconnectContext attempt connect without waiting for its leftover delay

The problem is that the scheduled automatic reconnect won’t ever reach its delay in doze mode(it might not even notice that its disconnected). This is why i wake up the device with an alarm when i think there might be an disconnect and call disconnect() on the client.

In the case that the client was connected this will work fine and the disconnectedlistener will schedule an autoreconnect and immediatly reconnect.

However if there was no connection, in the disconnectlistener an autoreconnect might have been scheduled already, its delay however might not be reached depending on how high it scaled due to the disconnection period.

i found this post:
cancel automatic reconnects
this wont help since its already scheduled.

Is there a way to do this?

Hi florian,

and welcome to our community forum. Thank you for your interest in MQTT and HiveMQ.

From your description it is not clear what you are trying to achieve, which workaround you have attempted to implement and what does not work. Perhaps you could describe your Use Case here and / or share your source code with explanations to make it clear what the issue is?

Thanks,
Dasha from HiveMQ Team

Hello Dasha, thanks for replying so quickly

Sry i ll try to explain in more detail.

I m developing an android app that is connected to an message broker via your mqtt client.
This app keeps an service with the mqttclient running even when the android device goes into low power modes. These low power modes will stop the cpu from running. However when messages come in the system will wake up the cpu and my service will acquire a wakelock stopping the cpu from shutting down for a short period and allowing it to do some work.

In order to make sure my device reconnects when it lost connection i m using a disconnectlistener which will call


context.getReconnector()
.reconnect(true)
.delay(delay, TimeUnit.SECONDS);


on disconnect. Delay keeps scaling with each attempt.

This works fine when the device is awake and not in low power modes. However when in low power modes there are multiple problems.

an loss of connection wont be noticed since there is cpu running that increments the timeout of the keepalive timer.

the autoreconnect timer wont run either → there wont be more reconnect attempts once the device goes back to sleep.

This is why i m waking up my service every 15 minutes using an alarm if there was no message from the broker(there should be one every 4 minutes). On this wakeup i dont know if i m connected or the client just believes to be, so i call disconnect().

If my client had a connection it gracefully disconnects and the autoreconnect instantly reconnects just fine.

However there can be the case that there has been no connectivity to the broker for a while, which means the client might be in the progress of reconnecting in delay seconds(which wont happen since cpu will shut down soon).

This is why i need to call connect from the client even though there is an autoreconnect scheduled to happen in delay seconds. Calling connect however will give me: “MQTT client is already connected or connecting.” error.

So basically what i want to do is let my client attempt to reconnect even if an automatic reconnect was already scheduled to happen in my disconnectlistener.

so in my current workaround i m simply creating a new client every time my alert fires.

//alert firing
starts++
if(client!=null) {
            try {
                //disconnect if client is connected
                client.disconnect().get();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
        client=null;
//some stuff happening
buildClient()
//do some subs
connectClient()

private void buildClient(){
        client = Mqtt3Client.builder()
                .identifier(manufacturer+model)
                .addConnectedListener(new MqttClientConnectedListener() {
                    int id=starts;
                    @Override
                    public void onConnected(MqttClientConnectedContext context) {
                        running=true;
                        Log.v(connectionLogTag, "connectedListener  "+ id);
                        lastConnectedAttempts=0;
                        if(startUpWakeLock.isHeld()&&id==starts){
                            startUpWakeLock.release();
                            Log.v(connectionLogTag, "startUpWakeLock released in connect");
                            if(!startUpWakeLock.isHeld())
                                Log.v(connectionLogTag, "startUpWakeLock not held anymore");
                        }else{
                            rescheduleAlarm(default_Alarm_Duration);
                        }

                    }
                }).addDisconnectedListener(new MqttClientDisconnectedListener() {
 // id used to identify and make sure only the current client may reconnect
                    int id=starts;
                    long maxDelay=4*60;
                    @Override
                    public void onDisconnected(MqttClientDisconnectedContext context) {
                        Log.v("currrThreadName","disconnect: "+Thread.currentThread().getName());
                        int contextAttempts=context.getReconnector().getAttempts();
                        String reason="disconnectedListener  "+id+" attempt "+contextAttempts+"    "+context.getCause().getMessage();

                        Log.v(connectionLogTag, reason);
 // if the id of the listener is = starts it may attempt to reconnect
                        if(starts==id){
                            int attempts=lastConnectedAttempts;
                            lastConnectedAttempts++;
                            if(context.getCause().getMessage().equals(onMainThreadError)){
                                if(contextAttempts>0){
                                    attempts=contextAttempts-1;
                                }
                            }

                            if(startUpWakeLock.isHeld() && attempts>1 ){
                                startUpWakeLock.release();
                                Log.v(connectionLogTag, "startUpWakeLock released in disconnect");
                                if(!startUpWakeLock.isHeld())
                                    Log.v(connectionLogTag, "startUpWakeLock not held anymore");
                            }
                            long delay=5 * attempts;
                            if(attempts>6){
                                delay=maxDelay;
                            }else{
                                if(attempts>2)
                                    delay=delay*attempts;
                            }
                            Log.v(connectionLogTag, "current attempts "+attempts+"     current delay "+delay);
                            context.getReconnector()
                                    .reconnect(true)
                                    .delay(delay, TimeUnit.SECONDS);
                            try {
                                reason=reason+" delay: "+delay;
                                LogInExternal(DISCONNECT_LOG_FILE,true,reason);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                    }
                })
                .serverHost(serverUni)//"134.60.71.110"
                .serverPort(1883)
                .buildAsync();

    }
 public void connectClient(){
        Log.v(connectionLogTag, "connecting with username: "+username+" and pw "+password);
        client.connectWith().cleanSession(false)
                .keepAlive(240)
                .send()
                .whenComplete((connAck, throwable) -> {
                     //will throw “MQTT client is already connected or connecting.” error since already scheduled reconnector
                    if (throwable != null) {
                        Log.v(connectionLogTag, "connect failure -> release");
                        Log.v(connectionLogTag, throwable.getMessage());
                    } else {
                        Log.v(connectionLogTag, "connect");

                    }
                });
    }

This approach has the drawback that when my device is in doze and has no connection every alert will spawn a new disconnectlistener of the new client. These will accumulate and once i have connectivity keep interrupting the currenct client by once reconnecting(since now their timers can run out).
Also i don’t know if all the used resources will be cleaned up at some point.

and the way i would like it to go instead is to keep my client and simply call connect even if there is a reconnect scheduled:

// we wake up our device in doze with an alarm and acquire a wakelock for a short period
if(alertingToTestConnection()){
            try {
                //asyncClient
                client.disconnect()
                        .whenComplete((connAck, throwable) ->{
                            // we end up here if we were connected already
// this means the disconnect happend and the disconnectlistener will cause a reconnect 
                            if (throwable != null) {
                                Log.v(connectionLogTag, "##############testing throwable");
                                Log.v(connectionLogTag, throwable.getMessage());
                            } else {
                                Log.v(connectionLogTag, "############## connected");
                            }
                        }).get(500,TimeUnit.MILLISECONDS);
            } catch (ExecutionException e) {

                connectClient();

                Log.v(connectionLogTag, "#############Exec excep");
                e.printStackTrace();
            } catch (InterruptedException e) {

                connectClient();

                Log.v(connectionLogTag, "#############Interrupt excep");
                e.printStackTrace();
            } catch (TimeoutException e) {
 // if we end up here we weren't connected but a reconnect will already be scheduled in delay seconds.
// however since we will go back in doze we want this reconnect to happen now 
// calling connect() wont work however since we are already trying to connect
                connectClient();

                Log.v(connectionLogTag, "#############Timeout excep");
                e.printStackTrace();
            }

            return START_STICKY;
        }

Is there any way to make this approach work? to let my client that has a reconnect scheduled from its disconnectlistener try to reconnect immediatly?
I saw that all calls to the methods of MqttClientReconnector of a disconnectlistener should only happen in the disconnectlistener onDisconnect(context) method. So i cant just reference it and call reconnect(true) (i tried it failed).