Paho MQTT Android 服务问题
Paho MQTT Android Service Issue
我正在开发的应用程序中实施 Paho MQTT Android 服务。在测试了Paho提供的示例应用程序后,我发现有一些地方我想改变。
https://eclipse.org/paho/clients/android/
应用程序服务似乎在应用程序完全关闭后关闭。我想在应用程序关闭后保留服务 运行,以防收到更多消息。我也在寻找一种方法,在收到新消息后将应用程序打开到特定的 activity .
这是消息到达时调用的回调之一,我尝试实现一个简单的 startActivity 来打开特定的 activity,但如果应用是 ,它就不起作用closed/no 更长 运行.
如果有人使用过 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);
}
如果您使用任务管理器关闭您的应用程序,我认为这是不可能的,因为 "fully closing" 该应用程序还将停止它包含的所有服务。即使服务已启动 "sticky",它也不会在我的设备上重新启动。如果您通过在最近的任务上滑动关闭应用程序来关闭该服务,该服务会保留 运行。有关详细信息,请参阅此处:Killing android application from task manager kills services started by app
但是,我认为另一个问题是即使服务仍然 运行 应用程序包含服务调用的回调对象。如果应用程序不再 运行 回调不再存在,因此永远不会被调用。
这是我如何实现它的高级视图。这已经 运行 投入生产几个月了,但不幸的是我没有代码,也不能 post 它。
- 我创建了一个托管
MQTTService
/mqttAndroidClient
的单例对象。这将 public 方法公开给 connect/disconnect,并包含用于接收消息的 MqttCallback
对象。它还处理连接丢失和所需的重试机制。这是最棘手的部分,但我不能 post 在这里。
- 我创建了一个
Application
对象,我在 onCreate()
中连接并在 onTerminate()
中关闭连接
- 我注册了一个
BroadcastReceiver
获取驻留在 Application
对象中的 BOOT_COMPLETED
操作,它有一个空的实现但是它启动了应用程序所以 mqtt 服务连接引导。
这消除了任何给定 activity 被 运行 接收消息的需要。它似乎也不会关闭应用程序,除非您在应用程序设置中 'force close' 它。这使得 since though 因为用户明确选择关闭它。
虽然这似乎不是问题的完整解决方案,但我会 post 我的解决方法以防它对某人有帮助。
对我来说,当用户将应用从最近使用的应用列表中滑出时,问题就开始了。如前所述 here 这样的操作不仅会杀死 activity,而是会杀死整个进程,包括 MqttService
。然后如线程中所述,Android 认识到您的服务应该重新启动并安排重新启动已终止的服务。但是,这并不意味着连接恢复,因为所有连接都绑定到 activity.
因此,除非您找到消除服务停止问题的方法,否则当用户决定退出应用程序时,您肯定会失去与代理的连接。
但这并不是世界末日,因为我们可以在失去连接后轻松重新连接。问题是,这次我们没有 activity 来执行所需的操作。您必须修改 Paho Android 服务库的源代码,或者以更简单的方式创建另一个服务。
所有连接都将在此新服务中进行,任何希望连接的活动都应与此服务进行通信。这样做的好处是我们可以把服务做成粘性的,即使用户刷我们的app把它杀了,它也会立即重启,我们只要重新连接就可以恢复。
作为这个非常简单的服务的演示:
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)
如您所见,一旦我关闭应用程序并且服务被终止,它会重新启动重新连接并保持活动状态,然后才找到。从这里开始,您应该可以完成剩下的工作。也许使用您新收到的消息创建一个通知,这将打开应用程序。只要记住在保证保持连接的新创建服务中执行所有操作即可。
我认为 Eclipse Paho 提供了执行此操作所需的一切。我可以刷我的应用程序,我的服务是运行。有关详细信息,请查看我在
上的回答
希望对你有所帮助
我知道这是对这个问题的较晚回答,但我想分享我所做的一切,因为它可能会对某人有所帮助。
我创建了自己的 Service
来管理与代理的连接,并始终为每个 android 设备维护一个连接实例。
重申解决方案的特点:
Key features of this solution:
- 服务只要存在就维护一个实例。
- 如果服务被终止,Android 将重新启动它(因为 START_STICKY)
- 服务可以在设备启动时启动。
- 服务在后台运行并始终连接以接收通知。
- 如果服务处于活动状态,再次调用
startService(..)
将触发其 onStartCommand()
。在这个方法中,我们简单地检查这个客户端是否连接到代理,如果需要的话 connect/reconnect。
查看非常详细的回答here。
我正在开发的应用程序中实施 Paho MQTT Android 服务。在测试了Paho提供的示例应用程序后,我发现有一些地方我想改变。
https://eclipse.org/paho/clients/android/
应用程序服务似乎在应用程序完全关闭后关闭。我想在应用程序关闭后保留服务 运行,以防收到更多消息。我也在寻找一种方法,在收到新消息后将应用程序打开到特定的 activity .
这是消息到达时调用的回调之一,我尝试实现一个简单的 startActivity 来打开特定的 activity,但如果应用是 ,它就不起作用closed/no 更长 运行.
如果有人使用过 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);
}
如果您使用任务管理器关闭您的应用程序,我认为这是不可能的,因为 "fully closing" 该应用程序还将停止它包含的所有服务。即使服务已启动 "sticky",它也不会在我的设备上重新启动。如果您通过在最近的任务上滑动关闭应用程序来关闭该服务,该服务会保留 运行。有关详细信息,请参阅此处:Killing android application from task manager kills services started by app
但是,我认为另一个问题是即使服务仍然 运行 应用程序包含服务调用的回调对象。如果应用程序不再 运行 回调不再存在,因此永远不会被调用。
这是我如何实现它的高级视图。这已经 运行 投入生产几个月了,但不幸的是我没有代码,也不能 post 它。
- 我创建了一个托管
MQTTService
/mqttAndroidClient
的单例对象。这将 public 方法公开给 connect/disconnect,并包含用于接收消息的MqttCallback
对象。它还处理连接丢失和所需的重试机制。这是最棘手的部分,但我不能 post 在这里。 - 我创建了一个
Application
对象,我在onCreate()
中连接并在onTerminate()
中关闭连接
- 我注册了一个
BroadcastReceiver
获取驻留在Application
对象中的BOOT_COMPLETED
操作,它有一个空的实现但是它启动了应用程序所以 mqtt 服务连接引导。
这消除了任何给定 activity 被 运行 接收消息的需要。它似乎也不会关闭应用程序,除非您在应用程序设置中 'force close' 它。这使得 since though 因为用户明确选择关闭它。
虽然这似乎不是问题的完整解决方案,但我会 post 我的解决方法以防它对某人有帮助。
对我来说,当用户将应用从最近使用的应用列表中滑出时,问题就开始了。如前所述 here 这样的操作不仅会杀死 activity,而是会杀死整个进程,包括 MqttService
。然后如线程中所述,Android 认识到您的服务应该重新启动并安排重新启动已终止的服务。但是,这并不意味着连接恢复,因为所有连接都绑定到 activity.
因此,除非您找到消除服务停止问题的方法,否则当用户决定退出应用程序时,您肯定会失去与代理的连接。
但这并不是世界末日,因为我们可以在失去连接后轻松重新连接。问题是,这次我们没有 activity 来执行所需的操作。您必须修改 Paho Android 服务库的源代码,或者以更简单的方式创建另一个服务。
所有连接都将在此新服务中进行,任何希望连接的活动都应与此服务进行通信。这样做的好处是我们可以把服务做成粘性的,即使用户刷我们的app把它杀了,它也会立即重启,我们只要重新连接就可以恢复。
作为这个非常简单的服务的演示:
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)
如您所见,一旦我关闭应用程序并且服务被终止,它会重新启动重新连接并保持活动状态,然后才找到。从这里开始,您应该可以完成剩下的工作。也许使用您新收到的消息创建一个通知,这将打开应用程序。只要记住在保证保持连接的新创建服务中执行所有操作即可。
我认为 Eclipse Paho 提供了执行此操作所需的一切。我可以刷我的应用程序,我的服务是运行。有关详细信息,请查看我在
希望对你有所帮助
我知道这是对这个问题的较晚回答,但我想分享我所做的一切,因为它可能会对某人有所帮助。
我创建了自己的 Service
来管理与代理的连接,并始终为每个 android 设备维护一个连接实例。
重申解决方案的特点:
Key features of this solution:
- 服务只要存在就维护一个实例。
- 如果服务被终止,Android 将重新启动它(因为 START_STICKY)
- 服务可以在设备启动时启动。
- 服务在后台运行并始终连接以接收通知。
- 如果服务处于活动状态,再次调用
startService(..)
将触发其onStartCommand()
。在这个方法中,我们简单地检查这个客户端是否连接到代理,如果需要的话 connect/reconnect。
查看非常详细的回答here。