XMPP 服务连接中断
Posted
技术标签:
【中文标题】XMPP 服务连接中断【英文标题】:XMPP service connection dies 【发布时间】:2014-03-09 10:50:49 【问题描述】:我正在用 Java 开发一个 android 聊天应用程序。现在我终于让我的服务开始工作了,但是一旦我完全终止应用程序,我的服务中的连接就会消失。
我使用 asmack 作为 XMPP 连接的库。目标是即使应用被用户杀死也能接收消息(因此它不在后台)。
当我使用前台服务时它确实工作,但我不想使用前台服务,因为内存使用率很高,而且我不希望前台消息出现在通知中心.
我的服务类
public class MessagingService extends Service
private final String TAG = "MessagingService";
private final IBinder mBinder = new MessagingBinder();
public Context context;
public XMPPConnection Connection;
public static Handler mHandler = new Handler();
private final int ONGOING_NOTIFICATION_ID = 2344;
@Override
public void onCreate()
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.d(TAG, "onStartCommand");
return START_STICKY;
@Override
public IBinder onBind(Intent intent)
Log.d(TAG, "onBind");
return mBinder;
@Override
public boolean onUnbind(Intent intent)
Log.d(TAG, "onUnbind");
return true;
@Override
public void onRebind(Intent intent)
super.onRebind(intent);
Log.d(TAG, "onRebind");
@Override
public void onDestroy()
public class MessagingBinder extends Binder
MessagingService getService()
Log.d(TAG + " - MessagingBinder", "getService");
return MessagingService.this;
public Boolean isConnected()
return (Connection != null);
public void Connect(final AuthorizeActivity authorize, final String username, final String password)
Thread XMPPConnect = new Thread(new Runnable()
public final String TAG = "XMPPConnect Thread";
@Override
public void run()
AndroidConnectionConfiguration connConfig = new AndroidConnectionConfiguration(Configuration.HOST, Configuration.PORT, Configuration.SERVICE);
SmackConfiguration.setDefaultPingInterval(100);
connConfig.setReconnectionAllowed(true);
connConfig.setSASLAuthenticationEnabled(true);
connConfig.setRosterLoadedAtLogin(true);
Connection = new XMPPConnection(connConfig);
try
Connection.connect();
Log.i(TAG, "Connected to " + Connection.getHost());
catch (XMPPException ex)
Log.e(TAG, "Failed to connect to " + Connection.getHost());
Log.e(TAG, ex.toString());
Connection = null;
if(authorize != null)
authorize.mServiceConnectCallback();
if(username != null && password != null)
Login(username, password, null);
);
XMPPConnect.start();
public void Login(final String username, final String password, final AuthorizeActivity authorize)
Thread XMPPLogin = new Thread(new Runnable()
public final String TAG = "XMPPConnect Thread";
@Override
public void run()
try
Connection.login(username, password);
Log.i(TAG, "Logged in as " + Connection.getUser());
Presence presence = new Presence(Presence.Type.available);
Connection.sendPacket(presence);
PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
Connection.addPacketListener(new PacketListener()
@Override
public void processPacket(Packet packet)
final Message message = (Message) packet;
if (message.getBody() != null)
final String fromName = StringUtils.parseName(message.getFrom());
Log.i(TAG, "Text Recieved " + message.getBody() + " from " + fromName );
mHandler.post(new Runnable()
public void run()
Receiver.recieveMessage(fromName, message.getBody());
if(!VisibilityHelper.IsVisible())
showNotification(fromName, message.getBody());
);
, filter);
catch (XMPPException ex)
Log.e(TAG, "Failed to log in as " + "test");
Log.e(TAG, ex.toString());
Connection = null;
if(authorize != null)
authorize.mServiceLoginCallback();
);
XMPPLogin.start();
public void showNotification(String from, String message)
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
CharSequence notiText = message;
long meow = System.currentTimeMillis();
Notification notification = new Notification(R.drawable.ic_launcher, notiText, meow);
Context context = getApplicationContext();
CharSequence contentTitle = from;
CharSequence contentText = message;
Intent notificationIntent = new Intent(context, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL;
int SERVER_DATA_RECEIVED = 1;
notificationManager.notify(SERVER_DATA_RECEIVED, notification);
public void Logout()
if(Connection.isConnected())
Log.i(TAG, "Logout");
Connection.disconnect();
public HashMap<String, String> getVCard(String user)
Log.d(TAG, "getVCard");
//String email = user + "@" + Configuration.HOST;
String email = user;
VCard card = new VCard();
ProviderManager.getInstance().addIQProvider("vCard", "vcard-temp", new VCardProvider());
try
card.load(MainActivity.mService.Connection, email);
String jabber_id = card.getJabberId();
String firstname = card.getFirstName();
String middlename = card.getMiddleName();
String lastname = card.getLastName();
HashMap<String, String> vcard = new HashMap<String, String>();
vcard.put("jabber_id", jabber_id);
vcard.put("firstname", firstname);
vcard.put("middlename", middlename);
vcard.put("lastname", lastname);
return vcard;
catch (XMPPException e)
e.printStackTrace();
return null;
public void retrieveContactsFromList()
if(this.isConnected())
Roster roster = Connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for(RosterEntry entry : entries)
Receiver.onRetrieveContactFromList(entry);
我启动服务的活动
public class ConnectionBinder extends FragmentActivity
private final String TAG = "ConnectionBinder";
public static MessagingService mService;
public boolean mBound = false;
public Database DB;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
if(!this.messagingServiceIsRunning())
startService(new Intent(this, MessagingService.class));
private boolean messagingServiceIsRunning()
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
if (MessagingService.class.getName().equals( service.service.getClassName()))
return true;
return false;
@Override
protected void onResume()
super.onResume();
doBindService();
@Override
protected void onPause()
super.onPause();
doUnbindService();
private void doBindService()
Intent intent = new Intent(this, MessagingService.class);
bindService(intent, mMessagingService, Context.BIND_AUTO_CREATE);
private void doUnbindService()
if (mBound)
unbindService(mMessagingService);
private void doXMPPLogin()
HashMap<String, String> user = DB.getUser();
mService.Connect(null, user.get("username"), user.get("password"));
private ServiceConnection mMessagingService = new ServiceConnection()
public void onServiceConnected(ComponentName className, IBinder service)
Log.d(TAG, "mMessagingService.onServiceConnected()");
MessagingBinder binder = (MessagingBinder) service;
mService = binder.getService();
mBound = true;
if(!mService.isConnected())
doXMPPLogin();
mService.retrieveContactsFromList();
public void onServiceDisconnected(ComponentName arg0)
Log.d(TAG, "mMessagingService.onServiceDisconnected()");
mBound = false;
;
【问题讨论】:
【参考方案1】:传统的 XMPP 实现(和 XMPP RFC)没有定义在客户端断开连接时维持持久用户“会话”的方法 - 当底层 TCP/IP 或 HTTP 连接丢失时,它们都会关闭用户会话。 另一方面,典型的 Android 环境具有“始终连接”的谷歌云服务,即使应用程序未连接,它也可以为您的应用程序传递消息。事实上,大多数聊天和社交网络应用程序都使用GCM 来通知用户新消息。 因此,根据您的需要,您需要在聊天应用程序的服务器端进行一些更改:
-
大多数 XMPP 服务器实现都能够存储在用户“离线”时收到的消息,并在用户再次连接时传递它。您可以“挂钩”离线消息接收并通过 Google Cloud Messaging 通知用户新消息的可用性,用户将在再次打开您的应用程序时收到它并且您的 XMPPConnection 将建立。
使用 XMPP Stream Management 扩展 - 如果您需要在多个用户重新连接之间共享同一个会话 - 并在用户再次打开您的应用程序时“恢复”上一个会话。您仍然应该通过 GCM 通知用户他的“会话”中的新事件。
您的服务器端 XMPP 软件应为每个用户设备保留 GCM 注册 ID,因此当用户设备在 GCM 中注册时 - 您需要通知服务器新注册的 ID - 这可以通过发送自定义
一些商业 XMPP 产品已经实现了上述步骤,并将向您出售“支持推送的 XMPP 服务”,正如我所描述的那样,它实际上是带有 GCM 后端的 XMPP 服务器。
【讨论】:
所以 Telegram 和 WhatsApp 等应用正在使用支持推送的 XMPP 服务? 他们有自定义的客户端连接协议(例如 Telegram 协议是基于 JSON 的,Whatsapp 使用某种 XMPP,但与标准不兼容),并且在客户端应用程序在后台。 还有一个问题。 WhatsApp 或/和 Telegram 是否总是使用 GCM(即使应用程序在前台)?或者当应用程序处于前台时,他们是否会切换到自己的服务。 您可以使用他们的source code 自己检查 :) @vitalyster 您能否解释一下:您可以“挂钩”离线消息接收并通过 Google Cloud Messaging 通知用户新消息的可用性。您的意思是像更改 XMPP 服务器代码以向 GCM 发送离线消息一样挂钩吗?以上是关于XMPP 服务连接中断的主要内容,如果未能解决你的问题,请参考以下文章
如何在Android中创建使Xmpp与XMPP服务器保持连接的服务?
通过 Strophe 附加(未连接)时是不是应该向 XMPP 服务器广播状态?
我无法在 XMPP 服务器中使用 jmeter 进行负载测试,我添加了所有插件并给出了 xmpp 连接、用于连接登录的采样器以及所有