当 wifi 断开连接并且不会重新连接时,应用程序、服务和 mqtt 崩溃

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

我正在制作一个带有 mqtt 客户端和后台服务的应用程序,当我收到某些 mqtt 消息时会发出通知。

我使用 Paho 库和服务作为客户端,只要我连接到 wifi 并且代理处于打开状态,一切都会完美运行。

我不想通过互联网访问代理,所以当没有可用的 wifi 时,客户端会断开连接,问题是当 wifi 再次连接时,mqtt 客户端不会重新连接。

我尝试了很多东西,但最新的测试是让处理程序检查是否有互联网以及是否通过 wifi,如果是,我再次启动 mqtt 服务器。

我在清单中拥有这些权限:

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

当我 运行 模拟器并断开 wifi 时,整个应用程序以及服务崩溃并出现以下错误

这是我正在使用的处理程序:

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

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

这是 isWifiConnected(如果没有我在应用程序中添加 Context context = this;,context.getSystemService 无法正常工作,我不知道这是否也是一个问题):

    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[=13=](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[=13=](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.

我知道你可能想要更多信息,所以不要我用错误的代码轰炸告诉我你需要什么来了解我做错了什么:)

错误代码似乎给出了答案,但由于我对 android 和 java 还很陌生。我不知道如何更改方法以使其正确。

你说:

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

  1. 您可以简单地在 Service#onCreate() 方法中使用 BroadcastReceiver 来监听 WIFI_STATE_CHANGED_ACTION 操作,如下所示。
  2. 此外,您还必须在 Service#onStartCommand() 中使用 return START_STICKY, 这样服务class在申请关闭后会变成运行

服务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. 您必须在清单文件中声明 org.eclipse.paho.android.service.MqttServiceYour Service Class
<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" />

希望对您有所帮助。
祝福