XMPP+Openfire4.5.1+Smack4.3.4+MySql

语言: CN / TW / HK

关于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尽量保证万无一失!

GitHub:http://github.com/baiyuliang/xmpp