0

When using MqttClient in AsnyTask, the client callback to MqttListener.messageArrived() is executed after Async.onPostExecute().

That means that the reply variable will be set after it is passed to the listener callback.

If onTaskCompleted is called from the MqttClient thread (from messageArrived()), an Exception is thrown from inside onTaskCompleted:

MqttException (0) - android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

public class MqttRequestHandler extends AsyncTask<Object, Void, String> implements MqttCallback {
    private OnTaskCompleted listener;
    String reply = "";

    public MqttRequestHandler(OnTaskCompleted listener) {
        this.listener = listener;
    }

    @Override
    protected String doInBackground(Object... params) {
        try {
            MqttClient client = new MqttClient("tcp://192.168.1.101", "test-client", new MemoryPersistence());
            client.setCallback(this);
            client.connect();
            client.subscribe(setup.topic);
        } catch (Exception e) {
            Log.e("MqttResponseHandler", e.toString());
        }

        return ""; //dummy, since we have to use the callback
    }

    @Override
    public void connectionLost(Throwable cause) {
        Log.d("MqttRequestHandler", "connectionLost: " + cause.toString());
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) {
        this.reply = message.toString(); // not empty
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
    }

    protected void onPostExecute(String dummy) {
        listener.onTaskCompleted(this.reply); // empty string!
    }
}

listener.onTaskCompleted(..) hangs when to doing ImageView.setImageBitmap(). The error message is received in connectionLost().

mwarning
  • 721
  • 5
  • 22
  • Try to set your callback for Activity like `client.setCallback(MainActivity.this);` not for AsyncTask – Dhruv Jul 27 '19 at 17:44
  • @Dhruv that does not solve the problem. It only puts the Mqtt callback interface onto the MainActivity class, but it is still called by the MqttClient thread. Maybe there is a way to tell mqtt to not to use it's own thread? – mwarning Jul 27 '19 at 18:58
  • replace this.reply… inside the messageArrived with listener.onTaskCompleted, remove the onPostExecute and use preExecute to set the Client and the callback. – Dominik Wuttke Jul 27 '19 at 21:31
  • @DominikWuttke I have tried that and get the same error. messageArrived(..) is called in the context of the the MqttClient thread und thus is then listener.onTaskCompleted(..). – mwarning Jul 27 '19 at 22:33
  • I have added some additional information. Maybe it helps. – mwarning Jul 27 '19 at 22:56
  • The Exception also appears when not using AsnyTask at all. That is, calling ImageView.setImageBitmap(..) from messageArrived(..) will block and CalledFromWrongThreadException is shown in connectionLost(..). – mwarning Jul 27 '19 at 23:03
  • you should make sure that the ontaskcompleted runs on UI thread https://stackoverflow.com/questions/11140285/how-do-we-use-runonuithread-in-android – Dominik Wuttke Jul 27 '19 at 23:35
  • @DominikWuttke ok, that did it! Feel free to submit an answer so you can get some Internet points. – mwarning Jul 28 '19 at 00:07
  • Added an Answer as a summary of the comments – Dominik Wuttke Jul 28 '19 at 00:24

1 Answers1

1

You can't Change a View from another thread, you have to make sure to Access the View from the Thread it was created in, this should be the UI Thread. You can refer to this Post

How do we use runOnUiThread in Android?

Inside your listener.onTaskCompleted(..) function you should make sure to Access your Views from the UI Thread.

If you want to use only the received String you can remove the OnPostexecute and do your onTaskcompleted inside your messagearrived callback.

Remove

protected void onPostExecute(String dummy) {
    listener.onTaskCompleted(this.reply); // empty string!
}

and Change

@Override
public void messageArrived(String topic, MqttMessage message) {
    this.reply = message.toString(); // not empty
}

to

@Override
public void messageArrived(String topic, MqttMessage message) {
    listener.onTaskCompleted(message.toString())
}
Dominik Wuttke
  • 535
  • 1
  • 4
  • 12