目录
一、发送方发消息异常的处理
1、如果发送方断网或掉线了,他还要发消息怎么办?(初步转化为如何去获取指定用户在线状态的问题,最终转化为消息发送失败来处理)
2、好,保证了发送方能发送消息,但是能发送消息不一定说消息就一定发送成功啊,那如果发送方发送消息失败了怎么办?
3、当接收方掉线的时候,发送方发消息给对方,服务端会给发送方发送一条type为error的消息。
二、接收方收消息异常的处理
1、XMPP消息流向的说明(或者说聊天记录的保存)
2、接收方在线的时候(在聊天界面,或者不在聊天界面但在前台运行App,或者后台运行App还没被杀死的时候)
3、拉取离线消息
4、隔三天后,删除与指定客服的聊天记录
三、和安卓通信、又拍云SDK
四、获取客服的在线状态
五、界面出现卡顿,tableView性能优化
六、难点:推送
一、发送方发消息异常的处理
1、如果发送方断网或掉线了,他还要发消息怎么办?
其实针对发送方我们只关心他是否在线,因为在线的话就能正常发出去消息,不在线的话我们就需要做异常处理。而断网只是一种现象,它最终导致的结果还是是发送方掉线,所以我们也可以把断网也归类为掉线来处理。
好,那如果发送方真的掉线了,他还要发消息,我们该怎么处理呢?
- 1、断线重连:第三篇的时候,我们已经做了断线重连的处理,这是一个基本操作,如果用户因为一些异常掉线了,我们必须尝试断线重连。
- 2、重连上了:如果断线重连成功了,那固然好,相当于正常状态,可以正常发消息了。
- 3、重连失败:如果重连失败了,也就是是说一直重连不上,甭管是什么原因导致重连不上,反正就是重连不上(比如断网了一直没恢复网络,服务器挂了一直重连不上等等),那么如果此时用户发消息,我们就应该拿到发送方用户的在线状态来判断到底要不要他发出去消息。这里为什么不是监听网络状态来决定用户要不要发消息呢?上面也说了,网络状态仅仅是导致用户掉线的一种可能性,还有别的乱七八糟的情况可能导致用户掉线。好,此处为了简洁有效,我们采取的办法是如果用户掉线,它发送消息时,就先提示它连接异常,当然我们可以做更细化的操作。
然后,问题来了,发送方就是当前登录的用户,那我们如何去获取当前登录用户的在线状态呢?前面的一篇文章中,我们只是提供了获取好友在线状态的方法,那个能同样获取自己的在线状态吗?答案是不能。
好,这个问题就转化为了如何获取指定用户在线状态的问题,下面第四小节是解决方案。这样同时解决了如何拉取到在线客服的问题,因为我们顶多是拉取到客户的电子名片,但是却无法拉取到客服是否在线,这样也就一并解决了。
2、好,保证了发送方能发送消息,但是能发送消息不一定说消息就一定发送成功啊,那如果发送方发送消息失败了怎么办?
我们在聊天界面有消息发送失败的回调,我们可以在这里根据业务做很多细节的处理,为了简单起见我们这里仅做提示。
// 消息发送失败
- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error
同时,在处理这个问题的时候,我们也发现了第1个问题,即用户离线了还要发消息这个问题,我们上面不是选择判断在发送消息之前判断一下用户的在线状态吗?现在好像不用这么做了,因为用户掉线了还发消息的话,消息是会发送失败的,因此也会走这里的回调,要都提示的话就会重复,不如一块在这里提示。因此第1个问题就最终转化为和这个问题一起处理了!
这也告诉我们一个解决问题的思路:如果我们发现了一个问题,要解决的是这个问题会导致的后果,那么可以尝试想想问题可能牵涉到哪个结尾处,从问题的结尾处着手解决,因为这样可能一并解决掉很多导致该结果的其它问题,因为结尾处只有一个嘛。但是如果我们发现了一个问题,要解决的是导致这个问题的原因,那么最好从该问题的源头出解决,因为这样可能会一并解决掉这个源头可能引起的其它很多问题,因为源头也只有一个。只有那些单个的小问题或者根本就不能称之为问题的问题,才解决问题本身。
但是第一个问题的转化,是不得不做的,也就是说根据指定的id去获取用户的在线状态是必须做的,因为我们要拉取到在线的客服来让用户和他们聊天。
3、当接收方掉线的时候,发送方发消息给对方,服务端会给发送方发送一条type为error的消息。
XMPPMessage的type属性,主要有5种类型:
- normal:类似于email,主要特点是不要求响应;
- chat:类似于qq里的好友即时聊天,主要特点是实时通讯;
- groupchat:类似于聊天室里的群聊;
- headline:用于发送alert和notification;
- error:如果发送message出错,发现错误的实体会用这个类别来通知发送者出错了;
刚开始采取的方法是把消息类型切换为normal,因为normal是一种不要求响应的消息,可以解决该问题。但是把消息类型改为normal,引起的另一个问题是安卓和iOS双端无法接受离线消息。
最终还是改回了chat类型,其实这个问题不是消息类型引起的,而是因为双端XMPPJID没统一好。
二、接收方收消息异常的处理
1、XMPP消息流向的说明(或者说聊天记录的保存)
在解决问题之前,我们先说明一下XMPP消息的流向,再次明确一下XMPP是一个C/S架构(客户端/服务端架构)。
XMPP消息的流向:
- 客户端A发送消息到openfire服务器的同时,XMPPFramework开源库会帮我们把这条消息保存到本地数据库(XMPPFramework内部自己完成,不需要我们的任何操作)。
- openfire服务器可能会帮我们保留一定时间的这条消息。
- openfire服务器检测用户B是否在线,在线的话直接把消息推送给B,如果B不在线,则等B上线后把消息推送给B,B接收到消息后,XMPPFramework也会将这条消息存储到本地数据库。
需要注意的是:XMPPFramework或者说openfire服务器并没有提供给我们客户端去服务器拉取聊天记录的API,所以如果一个账号去新设备登录或者卸载掉了App,聊天记录都会消失掉。如果有需求要从服务器拉取聊天记录,可以在自己服务器处理。
一些三方的SDK也是这么做的,比如环信、融云等等。
2、接收方在线的时候(在聊天界面,或者不在聊天界面但在前台运行App,或者后台运行App还没被杀死的时候)
这种情况,暂时还没有发现问题。
3、拉取离线消息
问题:接收方离线的情况下,上线后拉取不到图片和音频消息。
解决:刚好我们把图片和音频都上传到了又拍云,拿回地址,当作字符串来传了,所以就通过这种办法解决了拉取不到离线消息的问题。
其它可能的可能的解决方案:
https://blog.csdn.net/a8221379/article/details/37704229
https://blog.csdn.net/huwenfeng_2011/article/details/43459039
https://blog.csdn.net/superyu1992/article/details/69389360
此外:我们还要注意,拉取下来的离线消息和在线消息的顺序可能会出错,所以不能直接把消息数组展示在界面上,而要根据消息的时间戳来排序,然后再做展示。
4、隔三天后,删除与指定客服的聊天记录
聊天记录都是CoreData的本地存储,和拉取指定联系人聊天记录的方法基本一样,只需要调用一下上下文来删除聊天记录就可以了。
三、和安卓通信、又拍云SDK
- 约定好消息的json格式,作为XMPPMessage的body,互传就可以了。
- 如果接收不到消息时,可确认一下XMPPJID的是否正确,也就是确保一下代码知道要把消息发送给谁。这里我们遇见一个问题就是XMPPJID的resource两端不一致,导致发送消息有时候收不到,改一致就可以了。
四、获取客服的在线状态
- 获取方法:
https://blog.csdn.net/newjueqi/article/details/8613125
http://www.igniterealtime.org/projects/openfire/plugins/presence/readme.html
五、界面出现卡顿,tableView性能优化
-
要显示的内容提前计算好,尽量少实时计算:
- 用户的头像也会随着滑动cell反复的拉取本地数据,所以可以事先一次拉取好,而cell只负责显示。
- 每个消息的时间原来都是时间戳,需要计算成时间后再显示,算时间这个据说在iOS里面很耗性能,此处直接计算好时间后显示。
- 图片消息一定要用SDWebImage来做,否则能卡死人。
-
计算tableView的高度并缓存:
- 因为这里要让文本消息自适应高度,所以我们可以来缓存文本的高度,而不必每次都计算,非常耗性能。可以把行高作为model的一个属性,也可以在controller里用一个字典来存储每个cell的高度,返回cell高度的时候先检查字典,有则返回,没有则计算后返回,并存储在字典里。