zoukankan      html  css  js  c++  java
  • ios-异步消息同步问题-典型使用场景: 微信私信界面

    前言

     在ios开发中常常会有聊天功能,一般简单聊天功能只传输文字,但是稍微复杂点儿会有图片发送功能了.最全而且可支持扩展的例如微信,qq 聊天功能了.
    传输方式各有千秋,如get,post,websocket,xmpp...等等
    但最终避免不了一个问题,消息在队列里怎么通知前台view层 处理各种动作 如(发送失败,发送中,已读,未读 等)

    正文

    正式文章之前,我希望各种看官提前了解并熟悉一些技术点:
    1.GCD (Grand Central Dispath)
    2.BLOCK
    3.dispatch queue
    4.sync / async

    I.  消息队列里,每个消息都有唯一messageid ,用户标记整个消息处理.

    II. 每个Cell 一定有 NSNotification 提供消息通知器.

    III. 每个Cell 必须唯一.

    IV.Cell不可以承载太多retain操作,不然会导致界面很卡

    CODE:

    一条"文本"消息发出去时,  MODEL层只对消息做本地缓存/NSMutableArray里插入操作,并不会直接导致界面刷新. 只是给这段消息obj标记为sending message.. 当cell dequeueReusableCellWithIdentifier 时,会在 

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    

    里调用执行相关消息处理(如 消息发送)  

     

    if([message.messageSendStatus intValue] == messageSendStatusWillBeSend)
    {
    .....

       [cell SendMessageRemoteImgOper:_objImgListOper WithMessage:dictionary type:messageType_text];
    }

    MARK: 

    _objImgListOper 是后台消息队列 obj.
    dictionary 是要传递的消息,以及消息类型,时间,发送者,messageGuid,消息是否读取状态,消息发送状态.
    messageType_text  文字类型


    TODO:
    SendMessageRemoteImgOper 函数里一般做两种处理:
    1.给当前cell 注册通知
    2.把消息放入队列开始处理

    [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(sendMessageSucc:)
                                                             name:_strSuccNotificationName
                                                           object:nil];
                [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(sendMessageFail:)
                                                             name:_strFailedNotificationName
                                                           object:nil];
     __block NSMutableDictionary *blockDict = [dict mutableCopy];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (_objRemoteImgListOper)
                    {
                        [_objRemoteImgListOper sendMessageGUID:guid ByDict:blockDict withProgress:nil];
                    }else{
                        // local
                        
                    }
                });
            });
        });
    注意:
    [_objRemoteImgListOper sendMessageGUID:guid ByDict:blockDict withProgress:nil]; 这个函数内部主要做同步(dispatch_sync)消息处理.
    至于why?? 看前言....

    - (void) sendMessageGUID:(NSString *)guid ByDict:(NSMutableDictionary*) dict withProgress:(id)progress
    {
        if (guid && guid.length > 0) {
            
            _strSuccNotificationName = [NSString stringWithFormat:@"RemoteImgOperListSucc%@", guid];
            _strFailedNotificationName = [NSString stringWithFormat:@"RemoteImgOperListFailed%@", guid];
            
            __block NSString *strBlockURL = [guid copy];
            __weak id progressBlock = progress;
            
            dispatch_sync(_queueRemoteImgOper, ^{
                BOOL bIsRequesting = NO;
                for (NSDictionary *dicItem in _arrRemoteImgOper)
                {
                    NSString *strElementURL = [dicItem objectForKey:STR_ListElementURL];
                    if (strElementURL && [strElementURL isEqualToString:strBlockURL])
                    {
                        RemoteImgOperator *objImgOper = [dicItem objectForKey:STR_ListElementRequest];
                        
                        if (progressBlock)
                        {
                            [objImgOper setProgressDelegate:progressBlock];
                        }else{}
                        
                        bIsRequesting = YES;
                        break;          // break loop
                    }else{}
                }
                
                if (!bIsRequesting)
                {
                    RemoteImgOperator *objImgOper = [[RemoteImgOperator alloc] init];
                    [objImgOper setDelegate:self];
                    NSMutableDictionary *dicElement = [[NSMutableDictionary alloc] init];
                    [dicElement setObject:[strBlockURL copy] forKey:STR_ListElementURL];
                    [dicElement setObject:objImgOper forKey:STR_ListElementRequest];
    //                [dicElement setObject:dict forKey:STR_ListElementDictary];
                    [_arrRemoteImgOper addObject:dicElement];
                    
                    [objImgOper sendMessage:guid withDict:dict progressDelegate:progress];
                    
                   if (_arrRemoteImgOper && _arrRemoteImgOper.count > _iListSize)
                    { 列表满,取消第一个的下载并推出。
                        NSDictionary *dicFirst = [_arrRemoteImgOper objectAtIndex:0];
                        if (dicFirst)
                        {
                            RemoteImgOperator *objOper = [dicFirst objectForKey:STR_ListElementRequest];
                            if (objOper)
                            {
                                [objOper cancelRequest];
                                objOper = nil;
                            }else{}
                        }else{}
                        [_arrRemoteImgOper removeObjectAtIndex:0];
                    }else{}
                }else{}
            });
        }
    }

    重要: 消息最终发送和处理处

    - (BOOL)sendMessage:(NSString *)strGUID withDict:(NSMutableDictionary * ) dict progressDelegate:(id)progress
    {
        BOOL bRet = NO;
        
        [self cancelRequest];
        if (strGUID && (strGUID.length > 0))
        {
            bRet = YES;
            
            [self cancelRequest];
            downloadProgressDelegate = progress;
            
            __block NSString * guid = [strGUID copy];
            __weak typeof(self) blockSelf = self;
            int messagetype = [DataHelper getIntegerValue:dict[@"messagetype"] defaultValue:0];
            NSString * userID = [DataHelper getStringValue:dict[@"userid"] defaultValue:@""];
            NSString * content = [DataHelper getStringValue:dict[@"text"] defaultValue:@""];
            NSString * strSrcURL = [DataHelper getStringValue:dict[@"fileSrc"] defaultValue:@""];
            switch (messagetype) {
                case messageType_text:
                case messageType_emj:
                {
                    NSDictionary * parames = @{@"uid":userID,@"content":content};
                    [[MLNetworkingManager sharedManager] sendWithAction:@"message.send" parameters:parames success:^(MLRequest *request, id responseObject) {
                        // {"push": false, "errno": 1, "result": {}, "cdata": "MWUEM", "error": "session not found"}
                        if ([responseObject[@"errno"] intValue] == 0) {
                            
                            NSDictionary * dic = responseObject[@"result"];
                            NSString * messageId = [tools getStringValue:dic[@"msgid"] defaultValue:nil];
                            if (messageId) {
                                
                                dict[@"messageId"] = messageId;
                                
                                if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(sendMessage:sendMsgSuccess:fromGuid:)])
                                {
                                    // delegate 通知获取成功
                                    [blockSelf.delegate sendMessage:blockSelf sendMsgSuccess:dict fromGuid:guid];
                                }
                            }
                        }else{
                            //error ..... session not found
                            if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(sendMessage:sendMsgFailed:fromGuid:)])
                            {
                                // delegate 通知获取失败
                                [blockSelf.delegate sendMessage:blockSelf sendMsgFailed:dict fromGuid:guid];
                            }
                        }
                        
                        
                    } failure:^(MLRequest *request, NSError *error) {
                        if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(sendMessage:sendMsgFailed:fromGuid:)])
                        {
                            // delegate 通知获取失败
                            [blockSelf.delegate sendMessage:blockSelf sendMsgFailed:dict fromGuid:guid];
                        }
                    }];
                }
                    break;
                    
                case messageType_image:
                {
                    //image 1是图片,2是声音,3是视频  4 map
                    [self setuploadRemoteFile:guid FromURL:strSrcURL fileType:1 withParems:dict];
                }
                    break;
                case messageType_map:
                {
                        //image map
                    [self setuploadRemoteFile:guid FromURL:strSrcURL fileType:1 withParems:dict];
                }
                    break;
                case messageType_audio:
                {
                        // file
                   [self setuploadRemoteFile:guid FromURL:strSrcURL fileType:2 withParems:dict];
                }
                    break;
                case messageType_contacts:
                {
                        // object contacts
                }
                    break;
                default:
                    break;
            }
            
            
        }
        else
        {
            bRet = NO;
        }
        
        return bRet;
    }
    /**
     *   所有类型文件上传 class
     *
     *  @param strSrcURL URL
     */
    -(void) setuploadRemoteFile:(NSString * ) guid FromURL:(NSString *)strSrcURL fileType:(int) typeindex withParems:(NSMutableDictionary* ) parems
    {
        __weak typeof(self) blockSelf = self;
        //获取上传token  有效时间 3600 S  = 1 hour....
        //MRAK: that can be upload every files
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        NSMutableDictionary *parameters=[[NSMutableDictionary alloc] init];
        [parameters setValue:parems[@"token"]  forKey:@"token"];
        [parameters setValue:@(typeindex) forKey:@"x:filetype"];
        [parameters setValue:parems[@"text"] forKey:@"x:content"];
        [parameters setValue:parems[@"length"] forKey:@"x:length"];
        [parameters setValue:parems[@"userid"] forKey:@"x:toid"];
        __block NSData * FileData;
        AFHTTPRequestOperation * operation =  [manager POST:@"http://up.qiniu.com/" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            // 1是图片,2是声音,3是视频
            switch (typeindex) {
                case 1:
                {
                    // 图片压缩处理
                    UIImage * image = [UIImage imageWithContentsOfFile:strSrcURL];
                    int Wasy = image.size.width/APP_SCREEN_WIDTH;
                    int Hasy = image.size.height/APP_SCREEN_HEIGHT;
                    int quality = Wasy/2;
                    UIImage * newimage = [image resizedImage:CGSizeMake(APP_SCREEN_WIDTH*Wasy/quality, APP_SCREEN_HEIGHT*Hasy/quality) interpolationQuality:kCGInterpolationDefault];
                      NSData * FileData = UIImageJPEGRepresentation(newimage, 0.5);
                    if (!FileData) {
                        FileData  = UIImageJPEGRepresentation(image, 0.5);
                    }
    //                NSData *FileData  =  [UIImage imageToWebP:newimage quality:75.0];
                    [formData appendPartWithFileData:FileData name:@"file" fileName:@"file" mimeType:@"image/jpeg"];
                }
                    break;
                case 2:
                {
                    FileData = [NSData dataWithContentsOfFile:strSrcURL];
                    [formData appendPartWithFileData:FileData name:@"file" fileName:@"file" mimeType:@"audio/amr-wb"]; //录音
                }
                    break;
                case 3:
                {
                    FileData = [NSData dataWithContentsOfFile:strSrcURL];
                    [formData appendPartWithFileData:FileData name:@"file" fileName:@"file" mimeType:@"audio/mp4-wb"]; //视频
                }
                    break;
                default:
                    break;
            }
        } success:^(AFHTTPRequestOperation *operation, id responseObject) {
                SLog(@"responseObject :%@",responseObject);        
            if ([responseObject[@"errno"] intValue] == 0) {
                NSDictionary * dic = responseObject[@"result"];
                NSString * messageId = [tools getStringValue:dic[@"msgid"] defaultValue:@""];
                NSString *url = [tools getStringValue:dic[@"url"] defaultValue:@""];
                parems[@"messageId"]  = messageId;
                parems[@"url"]  = url;
                if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(sendMessage:sendMsgSuccess:fromGuid:)])
                {
                    // delegate 通知获取成功
                    [blockSelf.delegate sendMessage:blockSelf sendMsgSuccess:parems fromGuid:guid];
                }
            }else{
                if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(sendMessage:sendMsgFailed:fromGuid:)])
                {
                    // delegate 通知获取失败
                    [blockSelf.delegate sendMessage:blockSelf sendMsgFailed:parems fromGuid:guid];
                }
            }
            
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(sendMessage:sendMsgFailed:fromGuid:)])
            {
                // delegate 通知获取失败
                [blockSelf.delegate sendMessage:blockSelf sendMsgFailed:parems fromGuid:guid];
            }
        }];
        [operation start];
    }
     
    稍后将会给出相关消息发送处理代码,但这只是我解决私信聊天的一种解决方式而已,可能我的做法是错误的,但可以作为大家实践取交集.
    githu url: git@github.com:nicolastinkl/RemoteMessageListOperator.git


    如果大家有ios相关问题可以直接邮件我 : nicolastinkl@gmail.com


  • 相关阅读:
    idea中git分支的使用
    常用的分布式事务解决方案
    分布式事务解决方案总结
    IDEA中Git的更新、提交、还原方法
    (超详细)使用git命令行将本地仓库代码上传到github或gitlab远程仓库
    Git 安装及用法 github 代码发布 gitlab私有仓库的搭建
    主机ping不通虚拟机,但是虚拟机能ping通主机
    Compile Graphics Magick, Boost, Botan and QT with MinGW64 under Windows 7 64
    windows
    mingw-w64线程模型:posix vs win32(posix允许使用c++11的std:: thread,但要带一个winpthreads,可能需要额外dll)
  • 原文地址:https://www.cnblogs.com/tinkl/p/3573454.html
Copyright © 2011-2022 走看看