之前用的smack版本早,也有些问题,这段重新整理了下
大致的功能点:
1.基础配置
2.聊天建立
3.离线消息、流管理
基于kotlin写的,不过区别也不是很大,方法都一样
引用库为:
implementation "org.igniterealtime.smack:smack-android-extensions:4.3.4"
implementation "org.igniterealtime.smack:smack-tcp:4.3.4"
首先配置基础信息:
val config = XMPPTCPConnectionConfiguration.builder()
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)//安全模式认证
.setXmppDomain(host)
.enableDefaultDebugger()
.setConnectTimeout(10_000)
.build()
connection = XMPPTCPConnection(config)
setXmppDomain放域名,enableDefaultDebugger是debug模式,方便调试,安全模式那个暂时按关闭
然后建立监听:
//ConnectionListener
connection.addConnectionListener(XmppConnectionListener())
//ReceiveListener
connection.addSyncStanzaListener(XmppReceiveListener(), StanzaFilter { true })
//SendListener
connection.addStanzaSendingListener(XmppSendListener(), StanzaFilter { true })
这三个分别是连接状态监听、接受包监听、发送包监听,后两个里的StanzaFilter是过滤器,有什么需要可以在里面定制
XmppConnectionListener就是连接状态,实现接口ConnectionListener,新版的ConnectionListener里只有四个方法,如下:
void connected(XMPPConnection connection);
void authenticated(XMPPConnection connection, boolean resumed);
void connectionClosed();
void connectionClosedOnError(Exception e);
其它三个没什么好说的,connectionClosedOnError注意一下,账号互踢那个就是在这里回调
if (e?.message?.contains("conflict") == true) {
//互踢
}
这样判断就ok
XmppReceiveListener和XmppSendListener都是实现StanzaListener接口,就一个方法
void processStanza(Stanza packet)
Stanza这个类就代表消息的对象,里面的结构不算复杂,如果想看看内容用toString就行
基础配置完成后,还有比较重要的一步,就是自动重连机制,关键类ReconnectionManager,方法如下:
// 允许自动连接
val reconnectionManager = ReconnectionManager.getInstanceFor(connection)
// 重联间隔5秒
reconnectionManager.setFixedDelay(5)
reconnectionManager.enableAutomaticReconnection()//开启重联机制
setFixedDelay单位是秒
完成这步配置之后,如果联通xmpp,然后断网再联网,正常情况XmppConnectionListener中会依次回调
connectionClosedOnError、connected
这样就重连成功了,如果是已经通过登录验证的,还会回调authenticated,配置完自动重连是无需再重新登录的
后面还可以配置ping,代码如下:
// 维持ping
PingManager.setDefaultPingInterval(10)
val pingManager = PingManager.getInstanceFor(connection)
// 监听连接状态
pingManager.registerPingFailedListener(PingListener())
PingListener实现接口PingFailedListener,里面只有一个pingFailed方法,ping失败会回调
到此为止基础配置和监听就完成了,后面就是建立连接与登录认证,方法如下:
//连接
connection.connect()
//登录
connection.login(name, pwd, Resourcepart.from(resource))
其中login里的name参数要注意,格式大概是类似123@xxx.com这样,只写id不行
最后resource参数意义类似空间,同一处资源空间内,账号不能重复登录,不同空间则可以
另外注意,这两个都是耗时操作,不能放在UI线程,并且遇到异常会抛出错误
与这两个方法相对的状态判断是connection.isConnected与connection.isAuthenticated
最终登录方法大致如下:
fun login(): Boolean {
if (isAuthenticated()) {
return true
}
if (!isConnected()) {
try {
connection.connect()
} catch (e: Exception) {
XmppLogUtils.logE("on connect $e")
}
}
if (!isConnected()) {
return false
}
try {
connection.login(name, pwd, Resourcepart.from(resource))
} catch (e: Exception) {
XmppLogUtils.logE("on login $e")
}
return isAuthenticated()
}
当isAuthenticated返回true,同时authenticated被调用时,就完成了整个连接和登录验证过程
聊天就很简单了,关键类ChatManager,参数需要对方的id,方法如下:
val chatManager = ChatManager.getInstanceFor(connection)
val jid = JidCreate.entityBareFrom(id)
val chat = chatManager.chatWith(jid)
这样就有了一个聊天的实例,然后通过addOutgoingListener和addIncomingListener监控消息动向
chatManager.addOutgoingListener(ChatSendListener())
chatManager.addIncomingListener(ChatReceiveListener())
发消息则是用chat的send方法
chat.send(msg)
msg既可以直接传字符串,也可以创建Message对象
Message stanza = new Message();
stanza.setBody(message);
stanza.setType(Message.Type.chat);
send(stanza);
简单的测试可以自己和自己聊,OutgoingListener监控到发出,IncomingListener监控受到,就代表整个流程ok了,退出的话用disconnect就可以
因为前面我们已经配置了自动重连,在中途断线的情况下,消息基本是不会丢失的,但是如果处于未连接状态时,有人发送了一些消息,那么再登录时,是接收不到的,这类的消息就是离线消息
smack本身提供了用于获取离线消息的类OfflineMessageManager,不知道为啥我用这个不行,可能是服务端没开启配置,不过方法还是在此记录一下
首先要在之前配置连接的时候,加一个setSendPresence(false)方法,简单可以理解为以离线状态登录
val config = XMPPTCPConnectionConfiguration.builder()
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)//安全模式认证
.setXmppDomain(host)
.enableDefaultDebugger()
.setConnectTimeout(10_000)
.setSendPresence(false)
.build()
然后在登录认证通过后,处理离线消息
val offlineManager = OfflineMessageManager(connection)
try {
XmppLogUtils.logX("supportsFlexibleRetrieval: " + offlineManager.supportsFlexibleRetrieval())
XmppLogUtils.logX("离线消息数量: " + offlineManager.messageCount)
val it = offlineManager.messages
for (item in it) {
XmppLogUtils.logX("离线: " + item.body)
}
offlineManager.deleteMessages()
connection.sendStanza(Presence(Presence.Type.available))
} catch (e: Exception) {
XmppLogUtils.logE("offlineManager $e")
e.printStackTrace()
}
步骤很简单,第一步用connection构建一个OfflineMessageManager实例,然后处理offlineManager.messages这些离线消息,处理完了之后用deleteMessages清空这些消息,不然下次还有,最后sendStanza(Presence(Presence.Type.available))表示上线
前面也说了,可能是由于服务器配置问题,这个方法在我这不好使,所以用了另一种方法,也就是流管理
流管理就是xep-0198号协议,有兴趣的自己查查,整体内容比较多,就不详细介绍了,只说用法
首先通过connection开启,要在连接之前配置
connection.setUseStreamManagement(true)
然后,在登录成功后,向服务器发送相关消息
val resumed = connection.streamWasResumed()
val enabled = connection.isSmEnabled
XmppLogUtils.logX("SM: " + resumed + " " + enabled)
if (resumed || enabled) {
try {
connection.sendSmAcknowledgement()
} catch (e: StreamManagementException.StreamManagementNotEnabledException) {
e.printStackTrace()
} catch (e: SmackException.NotConnectedException) {
e.printStackTrace()
}
} else {
try {
connection.sendNonza(StreamManagement.Enable(true))
} catch (e: SmackException.NotConnectedException) {
e.printStackTrace()
}
}
connection.sendStanza(Presence(Presence.Type.available))
简单两步就ok了
配置完可以试试,这时就能收到离线消息了,而且相比OfflineMessageManager更不容易丢消息,因为有一些特殊情况,没收到的消息不会被判断为离线消息,就像A掉线的瞬间,如果服务器没有及时确认A已经离线,就不会把发给A的消息认定为离线消息,这样OfflineMessageManager里是没有的,但通过xep-0198就能收到这类的消息,具体实现机制自行了解去。
后面一些消息去重,消息回执,时效性判断之类的,就自行处理了,跟主线无关,到此基本的xmpp使用就OK了。
网友评论