【问题标题】:App, service and mqtt crashes when wifi disconnects and wont reconnect当 wifi 断开连接并且不会重新连接时,应用程序、服务和 mqtt 崩溃
【发布时间】:2019-08-21 20:07:43
【问题描述】:

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

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

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

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

我在 Manifest 中拥有这些权限:

    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$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.

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

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

【问题讨论】:

    标签: java android mqtt mosquitto paho


    【解决方案1】:

    你说:

    问题是当wifi再次连接时,mqtt客户端不会重新连接。

    1. 您可以简单地在您的Service#onCreate() 方法中使用BroadcastReceiver 来监听WIFI_STATE_CHANGED_ACTION 操作,如下所示。
    2. 您还必须在您的Service#onStartCommand() 中使用return START_STICKY, 这样服务类将在应用程序关闭后运行。

    服务类:

    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. 您必须在 Manifest 文件中声明 org.eclipse.paho.android.service.MqttService 和 Your 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" />
    

    希望对你有所帮助。
    最好的祝福

    【讨论】:

    • 感谢 Ghasem Sadeghi 先生,实际上我有点困惑,你能告诉我如何在我的活动中访问此服务类或如何使用此服务类订阅 Chanel
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多