问题
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.
- You can simply use a
BroadcastReceiver
in yourService#onCreate()
method that is listening toWIFI_STATE_CHANGED_ACTION
action as below. - Also you have to use
return START_STICKY
in yourService#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();
}
}
- You have to declare
org.eclipse.paho.android.service.MqttService
andYour 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