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


  • 相关阅读:
    hdoj2187:悼念512汶川大地震遇难同胞 (贪心)
    2.0其它之Transform详解,以及UIElement和FrameworkElement的常用属性
    2.0外观之样式, 模板, 视觉状态和视觉状态管理器
    2.0图形之Ellipse, Line, Path, Polygon, Polyline, Rectangle
    2.0控件之ListBox, MediaElement, MultiScaleImage, PasswordBox, ProgressBar, RadioButton
    2.0画笔之SolidColorBrush, ImageBrush, VideoBrush, LinearGradientBrush, RadialGradientBrush
    2.0图形之基类System.Windows.Shapes.Shape
    2.0交互之鼠标事件和键盘事件
    2.0控件之ScrollViewer, Slider, StackPanel, TabControl, TextBlock, TextBox, ToggleButton
    2.0交互之InkPresenter(涂鸦板)
  • 原文地址:https://www.cnblogs.com/tinkl/p/3573454.html
Copyright © 2011-2022 走看看