XMPP+Openfire4.5.1+Smack4.3.4+MySql
关于XMPP,14年的时候就已经写过几篇博客了,服务器使用Openfire,安卓客户端使用asmack.jar,不过asmack现在已经停止维护,而Openfire和配套的smack.jar则一直在更新,所以这篇博客主要来写如何用最新的openfire4.5.1+smack4.3.4完成XMPP的初步实现!
效果如下图:
上面为安卓客户端和PC端的Spark实现即时聊天,当然可以手机和手机聊。相关资源下载地址:http://igniterealtime.org/downloads/index.jsp
至于Openfire的安装方法,基本跟之前没什么变化,可以参考之前的文章有介绍,在使用Openfire前,请务必安装好数据库,本例中使用的是MySql,安卓完成后,启动Openfire: 客户端:
数据库(数据库名自定义):
表示安装成功!
可以开始写安卓端APP了!
Smack.jar中的方法跟之前的asmack有相似也有区别,首先说Smack最新的登录方式如下:
```javascript InetAddress addr = InetAddress.getByName(Const.XMPP_HOST); HostnameVerifier verifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return false; } }; DomainBareJid serviceName = JidCreate.domainBareFrom(Const.XMPP_DOMAIN);//Domain 服务器名称 XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() .setHost(Const.XMPP_HOST) .setPort(5222) .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled) .setXmppDomain(serviceName) .setHostnameVerifier(verifier) .setHostAddress(addr) .setConnectTimeout(30000) .build(); xmpptcpConnection = new XMPPTCPConnection(config); //开启重联机制 // ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(xmpptcpConnection); // reconnectionManager.setFixedDelay(5); // reconnectionManager.enableAutomaticReconnection(); //连接 xmpptcpConnection.connect(); //登录 xmpptcpConnection.login(PreferencesUtils.getSharePreStr(context, Const.ACCOUNT), PreferencesUtils.getSharePreStr(context, Const.PWD)); if (xmpptcpConnection.isAuthenticated()) {//登录成功
} else {//登录失败
}
``` 端口5222固定的,其它重要的两个参数:host域名和Domain服务器名称:这是在安装openfire已经设置好的:
这里我将两个名称设置一样了!
注册方法,只要在connect成功后,调用以下方法即可:
javascript
AccountManager accountManager = AccountManager.getInstance(xmpptcpConnection);
accountManager.sensitiveOperationOverInsecureConnection(true);
accountManager.createAccount(Localpart.from(username), pwd);
注意XMPP连接等方法需要线程中进行,切记!
监听连接状态addConnectionListener:
```javascript ConnectionListener connectionListener = new ConnectionListener() { @Override public void connected(XMPPConnection connection) {
}
@Override
public void authenticated(XMPPConnection connection, boolean resumed) {
}
@Override
public void connectionClosed() {
}
@Override
public void connectionClosedOnError(Exception e) {
}
};
``` 监听消息接收:
```javascript ChatManager.getInstanceFor(xmpptcpConnection).addIncomingListener(new IncomingChatMessageListener() { @Override public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
}
})
监听消息发送:
javascript
ChatManager.getInstanceFor(xmpptcpConnection).addOutgoingListener(new OutgoingChatMessageListener() {
@Override
public void newOutgoingMessage(EntityBareJid from, Message message, Chat chat) {
}
})
``` 发送消息:
javascript
EntityBareJid jid = JidCreate.entityBareFrom(to + "@" + Const.XMPP_DOMAIN);
Chat chat = XMPPConnectionUtil.getChatManager().chatWith(jid);
if (chat != null) {
Message message = new Message();
message.setBody(msg);
chat.send(message);
}
接收离线消息:
首先看上面登录的方法中有设置:
javascript
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
此方法表示以离线状态登录,这样才能去接收离线消息,在登录成功后:
javascript
OfflineMessageManager offlineMessageManager = new OfflineMessageManager(XMPPConnectionUtil.getInstance());
List<Message> messageList = offlineMessageManager.getMessages();
for (Message message : messageList) {
LogUtil.e("离线消息>>" + message.getBody());
}
offlineMessageManager.deleteMessages();
XMPPConnectionUtil.getInstance().sendStanza(new Presence(Presence.Type.available));
离线消息获取完毕后,将登录状态设置为上线available。
由于smack自带的重连机制并不好用,所以需要我们自己写靠谱的重连机制,我们需要在ConnectionListener做监听,一旦发现连接错误等,变启动重连机制,本项目中做法:
```javascript @SuppressLint("HandlerLeak") public Handler handler = new Handler() { @Override public void handleMessage(@NonNull android.os.Message msg) { super.handleMessage(msg); LogUtil.e("重新连接>>"); XMPPConnectionUtil.login(mContext, new XMPPConnectionUtil.OnLoginListener() { @Override public void onLogin(boolean isLogin, String error) { if (!TextUtils.isEmpty(error) && error.contains("already logged")) { handler.removeMessages(0); return; } if (isLogin) {//连接成功 LogUtil.e("连接成功>>"); XMPPConnectionUtil.getInstance().addConnectionListener(connectionListener); XMPPConnectionUtil.getChatManager().addIncomingListener(incomingChatMessageListener); XMPPConnectionUtil.getChatManager().addOutgoingListener(outgoingChatMessageListener); handler.removeMessages(0); } else { LogUtil.e("连接失败>>"); handler.sendEmptyMessageDelayed(0, 5000); } } }); } };
/**
* 连接状态监听
*/
ConnectionListener connectionListener = new ConnectionListener() {
@Override
public void connected(XMPPConnection connection) {
handler.removeMessages(0);
LogUtil.e("connected>>");
}
@Override
public void authenticated(XMPPConnection connection, boolean resumed) {
handler.removeMessages(0);
LogUtil.e("authenticated>>" + resumed);
}
@Override
public void connectionClosed() {
removeListener();
handler.sendEmptyMessage(0);
LogUtil.e("connectionClosed>>");
}
@Override
public void connectionClosedOnError(Exception e) {
removeListener();
handler.sendEmptyMessage(0);
LogUtil.e("connectionClosedOnError>>" + e.getMessage());
}
};
``` 项目中也包含了查询用户和好友的方法,简单的基础封装,保持XmppConnect对象单例模式,另外,我将接收消息以及监听连接状态方法写在了MainActivity,而不是像之前一样写在Service,是因为Service管理越来越严格,很容易被杀掉即便应用是打开状态,所以写在ManActivity尽量保证万无一失!