App, service and mqtt crashes when wifi disconnects and wont reconnect

半世苍凉 提交于 2019-12-02 09:30:29

问题


I'm making an app with a mqtt client and a background service that makes a notification when I get certain mqtt message.

I use Paho library and service as the client and everything works perfectly as long as I am connected to wifi and the broker is on.

I don't want access to the broker over internet so when no wifi is available the client is disconnected, the problem is that when the wifi connects again the mqtt client wont reconnect.

I have tried many things but the latest test was to make a handler check if there is internet and if its via wifi, and if it is I start the mqtt server again.

I have these permissions in Manifest:

    android.permission.INTERNET"
    android:name="android.permission.ACCESS_NETWORK_STATE"
    android.permission.WAKE_LOCK"

When I run the emulator and disconnect the wifi now the entire app crashes as well as the services with the error below

This is the handler I'm using:

    handler.postDelayed(new Runnable(){
        public void run(){
            if (isWifiConnected()) {
                startMqtt();
            }

            handler.postDelayed(this, delay);
        }
    }, delay);

Here is the isWifiConnected (the context.getSystemService did not work without me adding Context context = this; in the app, I don't know if that is a problem as well):

    private boolean isWifiConnected() {
        boolean isWifi;
        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();

        return isWifi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
    }
D/AlarmPingSender: Unregister alarmreceiver to MqttServicephone
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
I/chatty: uid=10085(com.iteda.nome) identical 85 lines
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
I/chatty: uid=10085(com.iteda.nome) identical 4 lines
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
I/chatty: uid=10085(com.iteda.nome) identical 36 lines
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
I/chatty: uid=10085(com.iteda.nome) identical 17 lines
W/Mqtt: Failed to connect to: tcp://192.168.1.39:1883Unable to connect to server (32103) - java.net.ConnectException: failed to connect to /192.168.1.39 (port 1883) from /:: (port 0) after 30000ms: connect failed: ENETUNREACH (Network is unreachable)
I/MqttConnection: Requesting Automatic reconnect using New Java AC
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.iteda.nome, PID: 7394
    java.lang.RuntimeException: Error receiving broadcast Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x4200010 (has extras) } in org.eclipse.paho.android.service.MqttService$NetworkConnectionIntentReceiver@5224905
        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1401)
        at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void java.util.Timer.cancel()' on a null object reference
        at org.eclipse.paho.client.mqttv3.MqttAsyncClient.stopReconnectCycle(MqttAsyncClient.java:1120)
        at org.eclipse.paho.client.mqttv3.MqttAsyncClient.reconnect(MqttAsyncClient.java:1057)
        at org.eclipse.paho.android.service.MqttConnection.reconnect(MqttConnection.java:1049)
        at org.eclipse.paho.android.service.MqttService.reconnect(MqttService.java:342)
        at org.eclipse.paho.android.service.MqttService$NetworkConnectionIntentReceiver.onReceive(MqttService.java:827)
        at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1391)
        at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
I/Process: Sending signal. PID: 7394 SIG: 9
Application terminated.

I know you probably want more information so instead of me bombarding with the wrong code tell me what you need to se what I have done really wrong :)

The error code seems to give the answer but since I'm quite new to android and java. I don't know how to change the approach to make it correct.


回答1:


You said:

the problem is that when the wifi connects again the mqtt client wont reconnect.

  1. You can simply use a BroadcastReceiver in your Service#onCreate() method that is listening to WIFI_STATE_CHANGED_ACTION action as below.
  2. Also you have to use return START_STICKY in your Service#onStartCommand(), So that the service class will be running after application is closed.

Service class:

public class MQTTService extends Service {

private MqttAndroidClient clientPhone;

@Override
  public void onCreate() {
    super.onCreate();
    registerReceiver();
    new Thread(() -> init()).start();
}

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

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

private void registerReceiver(){
    m_ScreenOffReceiver = new BroadcastReceiver(){
        @Override
        public void onReceive(final Context context, Intent intent){
            //Log.d(TAG,"onReceive of Wifi_State_Change called");
            if(intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION))
            {
                int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
                if(wifiState != WifiManager.WIFI_STATE_ENABLED)
                    return;

                final WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
                new Handler().postDelayed(() -> {
                        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                        String ssid = wifiInfo.getSSID();
                        //Toast.makeText(context, "active wifi:"+ssid, Toast.LENGTH_SHORT).show();

                        //You can connect to the your mqtt broker again:
                        connectMQTT();
                }, 10000);
            }
        }
    };

    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    registerReceiver(m_ScreenOffReceiver, intentFilter);
}

private void init() {

    clientPhone = new MqttAndroidClient(this, "tcp://IP:PORT", "Your-CLIENT-ID");       
    //clientPhone = new MqttAndroidClient(this, "ssl://IP:PORT", "Your-CLIENT-ID");

    clientPhone.setCallback(new MqttCallback() {
        @Override
        public void connectionLost(Throwable cause) {
            //do something - for example reconnnect again
        }

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            //you can do everything with the received message from broker here
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {
            //do something
        }
    });
}

private MqttConnectOptions getOptions(){

    if(clientPhone.getServerURI().contains("ssl")) {
        //set ssl config.for example:
                //options.setSocketFactory(clientPhone.getSSLSocketFactory(YOUR_KEYSTORE_FILE, "YOUR_KEYSTORE_PASSWORD"));
                //...
    }
    options.setKeepAliveInterval(...);
    options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
    options.setAutomaticReconnect(true);
    options.setCleanSession(...);
    //options.setWill(...);
    options.setUserName(...));
    options.setPassword(...);
    return options;
}

private void connectMQTT() {
    try {
        //getOptions is a method that returns your MqttConnectOptions object
        IMqttToken token = clientPhone.connect(getOptions());
        token.setActionCallback(new IMqttActionListener() {
            @Override
            public void onSuccess(IMqttToken asyncActionToken) {
                //do something
            }
            @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
            @Override
            public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                //do something
            }
        });
    } catch (MqttException e) {
                //do something
        e.printStackTrace();
    }
}


@Override
public void onDestroy() {
    if(clientPhone!=null) {
        /*unregisterResources is needed,otherwise receive this error:
          has leaked ServiceConnection org.eclipse.paho.android.service.MqttAndroidClient*/
        try {
            clientPhone.unregisterResources();
            clientPhone.close();
            clientPhone.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    unregisterReceiver(m_ScreenOffReceiver);
    m_ScreenOffReceiver = null;
        ...
    super.onDestroy();
}

}
  1. You have to declare org.eclipse.paho.android.service.MqttService and Your Service Class in your Manifest file:
<service
            android:name="org.eclipse.paho.android.service.MqttService"
            android:enabled="true" />
<service
            android:name="YOUR-MQTT-SERVICE-CLASS"
            android:enabled="true"
            android:exported="false" />

I hope this helps you.
Best wishes



来源:https://stackoverflow.com/questions/57598554/app-service-and-mqtt-crashes-when-wifi-disconnects-and-wont-reconnect

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!