美文网首页
iOS IM消息设计:如何提升弱网的体验;保证消息不重复、不丢失

iOS IM消息设计:如何提升弱网的体验;保证消息不重复、不丢失

作者: 某非著名程序员 | 来源:发表于2021-02-04 12:40 被阅读0次

    一、如何保证弱网情况下消息的响应速度

    弱网

    弱网关乎的用户的体验的问题:进一个聊天窗口,一直在转圈,体验是非常差的。

    场景

    1. 无网
      无网的状态是可以判断的,网络不正常只有DB有什么展示什么,也许消息断层。但可以理解。

    2. 网络良好
      这种情况消息完全可以正常展示,哪怕你每次进会话请求都ok。
      但要考虑到为节流、节省电量。

    3. 弱网
      这个时候可能是客户端问题:如2g网络、3g网络、明明是4g却只有一格、两格信号;wifi明明连上,网络却不好。

    解决方案

    本文的方案思想基于算法:合并区间
    拉取过的消息,缓存本地,不断合并区间来确保消息的可信。避免看过的消息再从网络拉取。
    难点:如何确保消息的连续性面临挑战。有网的情况下:必须确保数据的有序、不丢失、不重复。我们能做到请求过的消息不再请求或者减少请求。

    消息模型

    消息模型

    例如:
    我们当前聊天展示的消息是[100-200],过了很长时间现在消息是300,那我们进会话回去拉取20条,即[280-300],那我们本地块就有两个[100-200],[280-300]。向上滑动加载20条历史数据,第二个块变成了[260-300]。如果继续加载历史数据,第二个会变成[200-300]。这个时候把第二个与第一个合并,本地块只有一个,[100-300]的消息。

    当下次看历史消息,消息在[100-300]这段不需要请求网络了。

    不断与原来区间合并,让本地可信区间越来越大,为我们所用。

    实际场景

    1. 推送,启动app或断网重连时服务端会推送10条消息,这个时候也需要与本地最大的块进行合并。
    2. 进聊天窗口,如果是合并过的,直接展示。如果没有展示过,则需要拉取网络进行展示,合并块。
    3. 拉取历史消息:需要与内存列表进行合并,同时需要与前一个DB块进行合并。

    效果

    1. 看过的消息,不再需要依靠网络。减少网络交互。
    2. 弱网或有网时,在本地可信区间内,体验是非常好的。

    二、如何确保界面消息不丢失、不重复、有序展示

    场景

    1. 进入会话先展示DB消息(可能不足一屏),再拉取服务端消息(有可能与DB展示的消息重合,也有可能是最新的20条),正常展示后可以加载历史消息
    2. 搜索进入的消息是中间的消息,可以向下从顶部加载历史消息,也可以向上从底部加载新消息。
    3. 断网重连后,服务端会推送10条消息。

    之前的做法

    有个变量判断是否从顶部拉取,还是从底部拉取。拉取回来的消息需要与界面上展示的消息进行合并。如果是顶部拉取的,则插入到原数组第一个位置;如果是底部拉取,则追加到数组的末尾。

    实际情况

    消息是可能存在重复的。例如我拉取了DB的10条,这个时候网络回来20条,网络回来的20条可能包含DB的10条,也可能不包含与DB的10条重合的,也可能没有交集。靠插入到第一个位置或追加到末尾的位置,已经靠不住了。
    追加末尾,只有插入的消息大于缓存中的最大一条才追加到末尾。导致消息丢失。

    出现偶现的bug

    1. 重新安装登录时,打开消息,发现有部分消息没有拉取到,退出重新进入后好了
    2. A呼叫B(B再桌面,在线状态),然后A挂断,B点击通知消息,进入会话框,发现消息未拉取,页面空白
    3. 杀死进程,进入APP,快速点击某个会话,界面上会丢失消息,且无法拉取到。返回列表,重新进入后,显示正常。
    4. iOS 14.3系统,点击锁屏中的通知消息,输入安全密码,进入会话框,消息没有拉取到(偶现)

    思考

    无论是原有的消息,还是拉取的消息,都是有序的。
    利用双指针算法来实现两个有序数组的合并。不用考虑消息重复,重叠,拼接是否有问题。

    /*
     合并两个有序的消息:如果存在重复,取其中一个,丢弃另一个
     */
    - (void)mergeCacheMsgs:(BLConversation *)conversation newMsgs:(NSArray *)newMsgs{
        NSMutableArray * cacheMsgs = conversation.messages;
        
        NSMutableArray * resultMsgs = [[NSMutableArray alloc] initWithCapacity:cacheMsgs.count+newMsgs.count];
        
        NSInteger i = 0;
        NSInteger j = 0;
        NSInteger k = 0;
        
        while (i<cacheMsgs.count && j<newMsgs.count) {
            NSInteger cacheMsgId = [[cacheMsgs[i] messageId] integerValue];
            NSInteger newMsgId = [[newMsgs[j] messageId] integerValue];
            if (cacheMsgId<newMsgId) {
                resultMsgs[k] = cacheMsgs[i++];
            }else if (cacheMsgId>newMsgId){
                resultMsgs[k] = newMsgs[j++];
            }else{
                resultMsgs[k] = cacheMsgs[i];
                i ++;
                j ++;
            }
            k ++;
        }
        
        while (i<cacheMsgs.count) {
            resultMsgs[k++] = cacheMsgs[i++];
        }
        
        while (j<newMsgs.count) {
            resultMsgs[k++] = newMsgs[j++];
        }
        
        [conversation.messages removeAllObjects];
        [conversation.messages addObjectsFromArray:resultMsgs];
        [self reloadConversationMessagesTimeline:conversation];
    }
    

    总结

    1. 有序数组的合并应用上了,代码清晰、易读。
    2. 再也不会有消息重复、重叠的问题,也不需要靠字典来去重。
    3. 没有变量来判断是拼接到第一个位置、还是追加到末尾。
    4. 时间复杂度:新的消息一般20条左右,时间复杂度为O(n+20),不会有任何性能问题。

    三、使用二分算法实现消息的快速查找

    场景

    消息已读、已听、撤回,需要找到内存中的消息对象,并修改属性;

    使用字典来实现消息查找

    数组结合字典,能够实现消息的快速查找,但是需要多维护一个字典。
    消息返回,发送消息,接收到推送消息,同步消息时需要先塞进字典;
    退出当前会话需要移除字典中的消息。

    思考

    仔细分析发现,数组展示的消息都是有序的,采用二分查找,效率很高,不需要维护额外的字典。

    - (BLMessage *)binSearchMsgWithMsgId:(NSString *)messageId{
        NSInteger mid = 0;
        
        NSInteger l = 0;
        NSInteger r = self.messages.count-1;
    
        while (l<=r) {
            mid = (l+r)>>1;
            if ([messageId integerValue]>[[self.messages[mid] messageId] integerValue]) {
                l = mid+1;
            }else if ([messageId integerValue]<[[self.messages[mid] messageId] integerValue]){
                r = mid-1;
            }else{
                return self.messages[mid];
            }
        }
        
        return nil;
    }
    

    小结

    在复杂的场景下,少维护一个全局变量,代码的复杂度会小很多。

    相关文章

      网友评论

          本文标题:iOS IM消息设计:如何提升弱网的体验;保证消息不重复、不丢失

          本文链接:https://www.haomeiwen.com/subject/fhkbtltx.html