Aws-sdk-android: AWSIotMqttManager.publishString with Callback and QoS1 service does not seem to work

Created on 25 Dec 2019  路  4Comments  路  Source: aws-amplify/aws-sdk-android

AWSIotMqttManager.publishString when used with QoS1 service does not work when the input argument includes a DeliveryCallback instance. Without the DeliveryCallback instance in the input arguments it works fine. When the callback is included, the message gets published, but the callback is never invoked. Further, though it is not an issue, it is an odd behaviour -- after the message gets published, the Mqtt connection is lost and reconnection happens thereafter. And this seems to happen everytime callback is invoked.

This is the same issue as "https://github.com/aws-amplify/aws-sdk-android/issues/449". But that issue was closed due to inactivity. I have tried to implement the same code as has been implemented in this issue.

If it would have run successfully, I think the code snippet inside of the method 'handleMessage' should have gotten executed. This does not happen in my case.

This is the snippet of code I have implemented:

publishMessage("some_topic", "some string", AWSIotMqttQos.QOS1);

@SuppressLint("HandlerLeak")
 private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Timber.d("inside HandlerClass instance of mHandler...");
            Object obj = msg.obj;
            int arg1 = msg.arg1;
            boolean flag = (arg1==0);
            Timber.d("userData: %s, Successful? %s", obj.toString(), flag);
            super.handleMessage(msg);
        }
};

private class DeliveryCallback implements AWSIotMqttMessageDeliveryCallback {
        /**
         * Callback interface to be implemented by application.
         *
         * @param status   New status of the publish message.
         * @param userData User defined data which was passed in as part of the publish call.
         */
        @Override
        public void statusChanged(MessageDeliveryStatus status, Object userData) {
            Timber.d("inside statusChanged method in DeliveryCallback...");
            Message msg = Message.obtain();
            msg.arg1 = (status == MessageDeliveryStatus.Success) ? 0 : 1;
            msg.obj = (StringBuffer) userData;
            mHandler.sendMessage(msg);
            if (status == MessageDeliveryStatus.Fail) {
                Timber.d("Delivery Failed");
                return;
            }
            Timber.d("Delivery Success");
        }
}

public void publishMessage(String topic, String message, AWSIotMqttQos qos) {
        try {
            String info[] = new String[2];
            info[0] = topic;
            info[1] = message;
            DeliveryCallback callback = new DeliveryCallback();
            mqttManager.publishString(message, topic, qos, callback, 1);
            //mqttManager.publishString(message, topic, qos);
        } catch (Exception e) {
            Timber.d("Publish error on topic " + topic + " with message: " + message);
        }
}

This is the log from my Run when using DeliverCallback:

D/C:LocalService:763: Trying connecting to MQTT Broker ...
D/C:LocalService:774: Status = Connecting
I/AWSIotMqttManager: onSuccess: mqtt connection is successful.
D/C:LocalService:767: Status = Connected
I/AWSIotMqttManager: delivery is complete
D/C:LocalService$DeliveryCallback:949: inside statusChanged method in DeliveryCallback...
W/AWSIotMqttManager: connection is Lost
I/AWSIotMqttManager: schedule Reconnect attempt 0 of 10 in 4 seconds.
D/C:LocalService:774: Status = Reconnecting
D/AWSIotMqttManager: TID: 2462 trying to reconnect to session
I/AWSIotMqttManager: attempting to reconnect to mqtt broker
D/AWSIotMqttManager: Setting up Callback for MqttClient
    mqtt reconnecting attempt 1
I/AWSIotMqttManager: Reconnect successful
    Auto-resubscribe is enabled. Resubscribing to previous topics.
D/C:LocalService:767: Status = Connected

Note: I am implementing this using a background Service (LocalService). The 'Status' here corresponds to 'AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus'.

This is the log from my Run when not using DeliverCallback:

D/C:LocalService:763: Trying connecting to MQTT Broker ...
D/C:LocalService:774: Status = Connecting
I/AWSIotMqttManager: onSuccess: mqtt connection is successful.
D/C:LocalService:767: Status = Connected
I/AWSIotMqttManager: delivery is complete

Thank you!

IoT Needs Info from Requester Usage Question closing-soon-if-no-response

Most helpful comment

@ansuman87 l was not able to reproduce the issue in an independent sample app but I was able to reproduce it using the code snippet you provided. The problem is due to the fact that you are passing an integer(1 in your example above) as userData in the publish call. This value is returned in the onStatusChanged() method of your callback where you try to cast it to StringBuffer.
msg.obj = (StringBuffer) userData;
This results in ClassCastException in the paho thread where the connection and publish is being handled. This causes connection and publish failure. Connection will be reestablished if autoReconnect is enabled(which is by default) but the publish will fail.

All 4 comments

@ansuman87 Thanks for bringing this up. I will investigate and get back to you.

@ansuman87 l was not able to reproduce the issue in an independent sample app but I was able to reproduce it using the code snippet you provided. The problem is due to the fact that you are passing an integer(1 in your example above) as userData in the publish call. This value is returned in the onStatusChanged() method of your callback where you try to cast it to StringBuffer.
msg.obj = (StringBuffer) userData;
This results in ClassCastException in the paho thread where the connection and publish is being handled. This causes connection and publish failure. Connection will be reestablished if autoReconnect is enabled(which is by default) but the publish will fail.

Thanks @desokroshan!
This seems to be a very naive mistake on my part. I had no clue what to do with the userData argument. And I used the same arguments as the previous post regarding this issue. If you don't mind can you let me know what purpose can it be used for? I know this is not the right place to ask. :D

@ansuman87 Glad to be of help! userData is user defined data which will be returned back to the user when the passed callback with message delivery status is invoked. It is used for maintaining context across different publish requests that are submitted to AWS IoT.

Was this page helpful?
0 / 5 - 0 ratings