Проблема со службой Paho MQTT для Android

Я реализую службу Paho MQTT для Android в разрабатываемом мной приложении. После тестирования образца приложения, предоставленного Paho, я обнаружил, что есть несколько вещей, которые я хотел бы изменить.

https://eclipse.org/paho/clients/android/

Служба приложений отключается после полного закрытия приложения. Я хотел бы, чтобы служба работала даже после закрытия приложения в случае поступления новых сообщений. Я также ищу способ открыть приложение для определенного действия после получения нового сообщения.

Вот один из обратных вызовов, который вызывается при поступлении сообщения. Я попытался реализовать простой startActivity для открытия определенного действия, но он не работает, если приложение закрыто / больше не работает.

Если кто-нибудь работал со службой PAHO MQTT Android, существует ли особый способ предотвратить остановку службы при закрытии приложения и как я могу повторно открыть приложение при получении сообщения?

    /**
   * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String,
   *      org.eclipse.paho.client.mqttv3.MqttMessage)
   */
  @Override
  public void messageArrived(String topic, MqttMessage message) throws Exception {

    // Get connection object associated with this object
    Connection c = Connections.getInstance(context).getConnection(clientHandle);

    // create arguments to format message arrived notifcation string
    String[] args = new String[2];
    args[0] = new String(message.getPayload());
    args[1] = topic + ";qos:" + message.getQos() + ";retained:" + message.isRetained();

    // get the string from strings.xml and format
    String messageString = context.getString(R.string.messageRecieved, (Object[]) args);

    // create intent to start activity
    Intent intent = new Intent();
    intent.setClassName(context, "org.eclipse.paho.android.service.sample.ConnectionDetails");
    intent.putExtra("handle", clientHandle);

    // format string args
    Object[] notifyArgs = new String[3];
    notifyArgs[0] = c.getId();
    notifyArgs[1] = new String(message.getPayload());
    notifyArgs[2] = topic;

    // notify the user
    Notify.notifcation(context, context.getString(R.string.notification, notifyArgs), intent,
        R.string.notifyTitle);

    // update client history
    c.addAction(messageString);

    Log.e("Message Arrived", "MESSAGE ARRIVED CALLBACK");

    // used to open the application if it is currently not active
    Intent i = new Intent(context, ConnectionDetails.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    i.putExtra("handle", clientHandle);
    context.startActivity(i);


  }

person Coova    schedule 16.02.2015    source источник
comment
Есть ли шанс, что вам удалось решить эту проблему?   -  person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 04.06.2015
comment
К сожалению, нет. Я перешел к другому примеру. Я отправлю решение, если найду его.   -  person Coova    schedule 04.06.2015
comment
Вы решили проблему? Или, по крайней мере, вы нашли другой, работающий (удовлетворительный) пример?   -  person Muhamed Huseinbašić    schedule 05.07.2015
comment
У меня точно такая же проблема.   -  person Jdruwe    schedule 05.08.2015
comment
У меня тоже есть такая проблема   -  person lawonga    schedule 07.08.2016


Ответы (4)


Я знаю, что это поздний ответ на этот вопрос, но я хотел бы поделиться тем, что я сделал, поскольку это может кому-то помочь.

Я создал свой собственный Service, чтобы управлять подключением к брокеру и всегда поддерживать один подключенный экземпляр для каждого устройства Android.

Повторяя особенности решения:

Ключевые особенности этого решения:

  1. Сервис поддерживает единственный экземпляр, пока он жив.
  2. Если служба отключена, Android перезапускает ее (потому что START_STICKY)
  3. Сервис может быть запущен при загрузке устройства.
  4. Сервис работает в фоновом режиме и всегда подключен для получения уведомлений.
  5. Если служба активна, повторный вызов startService(..) вызовет ее onStartCommand(). В этом методе мы просто проверяем, подключен ли этот клиент к брокеру, и подключаемся / повторно подключаемся при необходимости.

Ознакомьтесь с полностью подробным ответом здесь.

person Pravin Sonawane    schedule 09.12.2016

Хотя это не похоже на полное решение проблемы, я опубликую свой обходной путь, если он кому-то поможет.

Для меня проблема начинается, когда пользователь вынимает приложение из списка последних приложений. Как упоминалось здесь, такое действие не только убивает активность, но и убивает весь процесс, включая MqttService. Затем, как упоминалось в потоке, Android распознает, что ваша служба должна быть перезапущена, и планирует перезапуск убитой службы. Однако это не означает восстановления соединения, поскольку все соединения были привязаны к активности.

Поэтому, если вы не найдете способ устранить проблему с остановкой службы, вы гарантированно потеряете соединение с брокером, когда пользователь решит удалить приложение.

Однако это не конец света, так как мы можем просто восстановить соединение после потери связи. Проблема в том, что на этот раз у нас нет активности для выполнения желаемого действия. Вам нужно либо изменить исходный код библиотеки служб Paho Android, либо, что гораздо проще, я создал другую службу.

Все соединения будут происходить в этой новой службе, и любые действия, связанные с подключением, должны взаимодействовать с этой службой. Преимущество этого заключается в том, что мы можем сделать службу «липкой», и даже если пользователь проведет пальцем по нашему приложению и убьет его, оно немедленно перезапустится, и мы сможем восстановить его, просто подключившись заново.

Итак, в качестве демонстрации этого очень простого сервиса:

public class MessagingService extends Service {
    private static final String TAG = "MessagingService";
    private MqttAndroidClient mqttClient;
    String deviceId;



    @Override
    public void onCreate() {
    }
    private void setClientID() {
        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        WifiInfo wInfo = wifiManager.getConnectionInfo();
        deviceId = wInfo.getMacAddress();
        if (deviceId == null) {
            deviceId = MqttAsyncClient.generateClientId();
        }
    }

    public class MsgBinder extends Binder {
        public MsgServ getService() {
            return MsgServ.this;
        }
    }

    public void doConnect(){
        // Using some of the Paho sample app classes
        String server = ConfigClass.BROKER_URI;
        MemoryPersistence mem = new MemoryPersistence();
        mqttClient = new MqttAndroidClient(this,ConfigClass.BROKER_URI,deviceId,mem);
        MqttConnectOptions conOpt = new MqttConnectOptions();
        String clientHandle = server + deviceId;
        Connection con = new Connection(clientHandle, deviceId, ConfigClass.BROKER_ADDRESS,
                                        ConfigClass.BROKER_PORT, this, mqttClient, false);
        conOpt.setCleanSession(false);
        conOpt.setConnectionTimeout(ConfigClass.CONN_TIMEOUT);
        conOpt.setKeepAliveInterval(ConfigClass.CONN_KEEPALIVE);
        conOpt.setUserName("testclient");
        conOpt.setPassword("password".toCharArray());
        String[] actionArgs = new String[1];
        actionArgs[0] = deviceId;
        final ActionListener callback =
                new ActionListener(this, ActionListener.Action.CONNECT, clientHandle,
                                   actionArgs);
        mqttClient.setCallback(new MqttCallbackHandler(this, clientHandle));
        mqttClient.setTraceCallback(new MqttTraceCallback());
        con.addConnectionOptions(conOpt);
        Connections.getInstance(this).addConnection(con);
        try {
            mqttClient.connect(conOpt, null, callback);
            Log.d("Con", "Connected");
        } catch (MqttException e) {
            Log.d("Con", "Connection failed");
            e.printStackTrace();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        doConnect();
        return START_STICKY;
    }

}

Журнал сервера:

1433455371: New client connected from 192.168.2.5 as ed:0a:2b:56:b5:45 (c0, k30, u'testclient').
1433455371: Sending CONNACK to ed:0a:2b:56:b5:45 (1, 0)
1433455375: Socket error on client ed:0a:2b:56:b5:45, disconnecting.
1433455377: New connection from 192.168.2.5 on port 1883.
1433455377: Client ed:0a:2b:56:b5:45 disconnected.
1433455377: New client connected from 192.168.2.5 as ed:0a:2b:56:b5:45 (c0, k30, u'testclient').
1433455377: Sending CONNACK to ed:0a:2b:56:b5:45 (1, 0)

Как вы можете видеть, как только я закрываю приложение и служба перестает работать, она перезапускает, переподключается и остается в живых, только потом находите. Отсюда вы сможете сделать все остальное. Возможно создание уведомления с вашим только что полученным сообщением, которое откроет приложение. Просто не забудьте сделать все во вновь созданном сервисе, который гарантированно поддерживает соединение.

person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 04.06.2015
comment
Не могли бы вы поделиться полностью рабочим примером (возможно, на github)? У меня много проблем, пытаясь заставить эту работу работать. - person Jdruwe; 05.08.2015
comment
Спасибо, я использовал этот дизайн для решения проблемы потерянных подключений / подписок, но теперь в действиях приложения мне нужно взаимодействовать со службой для публикации материалов, как мне это сделать? - person Japheth Ongeri - inkalimeva; 09.09.2016
comment
@inkalimeva См. Как настроить связь службы Android с Activity. - person ᴘᴀɴᴀʏɪᴏᴛɪs; 09.09.2016

Если вы закроете приложение с помощью диспетчера задач, я не думаю, что это возможно, поскольку «полное закрытие» приложения также остановит все службы, которые оно содержит. Несмотря на то, что служба запущена «липко», она не запускается повторно на моем устройстве. Если вы закроете приложение, проведя его пальцем по недавним задачам, служба продолжит работу. См. Здесь для получения дополнительной информации: Удаление приложения Android из диспетчер задач убивает службы, запущенные приложением

Однако я думаю, что другая проблема заключается в том, что даже если служба все еще работает, приложение содержит объекты обратного вызова, которые вызываются службой. Если приложение больше не работает, обратный вызов больше не существует и, следовательно, никогда не вызывается.

Вот общее представление о том, как я это реализовал. Он работает в производстве уже несколько месяцев, но, к сожалению, у меня нет кода, и я не могу его опубликовать.

  • Я создал одноэлементный объект, содержащий _1 _ / _ 2_. Он предоставляет открытые методы для подключения / отключения и содержит объект MqttCallback, используемый для получения сообщений. Он также обрабатывает потерю соединения и необходимые механизмы повторной попытки. Это самая сложная часть, но я не могу ее здесь опубликовать.
  • Я создал объект Application, подключаюсь в onCreate() и закрываю соединение в onTerminate()
  • Я зарегистрировал BroadcastReceiver, который получает действие BOOT_COMPLETED, которое находится в объекте Application, имеет пустую реализацию, но разворачивает приложение, поэтому служба mqtt подключается при загрузке.

Это избавляет от необходимости запускать какое-либо действие для получения сообщения. Он также кажется устойчивым к закрытию приложения, за исключением случаев, когда вы «принудительно закрываете» его в настройках приложения. Это происходит с тех пор, как пользователь явно решил закрыть его.

person Hugh Jeffner    schedule 20.02.2015
comment
Меня интересует решение. Но вы сказали: если вы закроете приложение, проведя его пальцем по недавним задачам, служба продолжит работу. Я не думаю, что он продолжает работать (по крайней мере, не так, как должен). После этого сообщения не приходят (никаких уведомлений, ничего). Если вы перезапустите приложение, сообщения, отправленные в эти темы, на которые вы подписаны, будут потеряны. :( - person Muhamed Huseinbašić; 05.07.2015
comment
Я обновил свой ответ, добавив более подробную информацию. Извините, я не могу опубликовать реальный код, но я надеюсь, что это дает вам представление о том, что происходит. - person Hugh Jeffner; 08.07.2015

Я думаю, что в Eclipse Paho есть все, что вам нужно для этого. Я могу пролистать свое приложение, и моя служба запущена. Дополнительные сведения см. В моем ответе на странице Пробуждение службы Android Paho MQTT активность

Надеюсь, это вам поможет.

person kdoteu    schedule 08.07.2015