前言

 在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, ), ^{
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
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 > ) { _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 > ))
{
bRet = YES; [self cancelRequest];
downloadProgressDelegate = progress; __block NSString * guid = [strGUID copy];
__weak typeof(self) blockSelf = self;
int messagetype = [DataHelper getIntegerValue:dict[@"messagetype"] defaultValue:];
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] == ) { 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: withParems:dict];
}
break;
case messageType_map:
{
//image map
[self setuploadRemoteFile:guid FromURL:strSrcURL fileType: withParems:dict];
}
break;
case messageType_audio:
{
// file
[self setuploadRemoteFile:guid FromURL:strSrcURL fileType: 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 :
{
// 图片压缩处理
UIImage * image = [UIImage imageWithContentsOfFile:strSrcURL];
int Wasy = image.size.width/APP_SCREEN_WIDTH;
int Hasy = image.size.height/APP_SCREEN_HEIGHT;
int quality = Wasy/;
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 :
{
FileData = [NSData dataWithContentsOfFile:strSrcURL];
[formData appendPartWithFileData:FileData name:@"file" fileName:@"file" mimeType:@"audio/amr-wb"]; //录音
}
break;
case :
{
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] == ) {
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

ios-异步消息同步问题-典型使用场景: 微信私信界面的更多相关文章

  1. 异步消息队列Celery

    Celery是异步消息队列, 可以在很多场景下进行灵活的应用.消息中包含了执行任务所需的的参数,用于启动任务执行, suoy所以消息队列也可以称作 在web应用开发中, 用户触发的某些事件需要较长事件 ...

  2. 循序渐进做项目系列(2):最简单的C/S程序——消息异步调用与消息同步调用

    上篇博客 循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法 实现了一个最简单的C/S程序,即让服务器来做加法.当时为了通俗易懂采用了消息异步调用的方式.今天我们要采用消息同步调用的方式 ...

  3. ZooKeeper典型应用场景

    ZooKeeper典型应用场景一览 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新.例 ...

  4. ZooKeeper典型应用场景一览

    原文地址:http://jm-blog.aliapp.com/?p=1232 ZooKeeper典型应用场景一览 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据 ...

  5. ZooKeeper典型应用场景(转)

    ZooKeeper是一个高可用的分布式数据管理与系统协调框架.基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题.网上 ...

  6. ZooKeeper典型应用场景概览

    ZooKeeper是一个高可用的分布式数据管理与系统协调框架.基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题.网上 ...

  7. kafka系列八、kafka消息重复和丢失的场景及解决方案分析

    消息重复和丢失是kafka中很常见的问题,主要发生在以下三个阶段: 生产者阶段 broke阶段 消费者阶段 一.生产者阶段重复场景 1.根本原因 生产发送的消息没有收到正确的broke响应,导致pro ...

  8. <zk的典型应用场景>

    Overview zk是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布与订阅. 另一方面,通过对zk中丰富的数据节点进行交叉使用,配合watcher事件通 ...

  9. 搞懂分布式技术6:Zookeeper典型应用场景及实践

    搞懂分布式技术6:Zookeeper典型应用场景及实践 一.ZooKeeper典型应用场景实践 ZooKeeper是一个高可用的分布式数据管理与系统协调框架.基于对Paxos算法的实现,使该框架保证了 ...

随机推荐

  1. spatialite-android-library 环境搭建

    spatialite-android-library项目介绍 搭建NDK开发环境 下载spatialite-android-library项目 搭建spatialite-android-library ...

  2. Linux下安装mysql5.6.11(找点有用的信息太费劲)(转)

    Linux下安装mysql5.6.11(找点有用的信息太费劲) (2013-04-25 10:25:09)     1.申请阿里云Linux服务器 昨天在阿里云申请了一个免费试用5天的Linux云服务 ...

  3. POJ2192 - Zipper(区间DP)

    题目大意 给定三个字符串s1,s2,s3,判断由s1和s2的字符能否组成字符串s3,并且要求组合后的字符串必须是s1,s2中原来的顺序. 题解 用dp[i][j]表示s1的前i个字符和s2的前j个字符 ...

  4. guslterFS

    Installing GlusterFS - a Quick Start Guide Purpose of this document This document is intended to giv ...

  5. Git简介:

    Git中文文档 1.详解在Visual Studio中使用git版本系统(图文) 2.GitExtensions下载地址:http://gitextensions.codeplex.com/ 3.Gi ...

  6. background-position也许你没考虑到

    设置背景图片时不知你有没有遇到过背景位置的困扰,有没有深入思考过,background-position到底是什么,下面请各位看看我的理解. 简而言之就一句话,默认图片左上角居元素左上角的坐标,例如: ...

  7. 3D案例,导航,导航升级版

    /*****************************百度钱包旋转变内容******************************/ <!DOCTYPE html> <htm ...

  8. 通达OA 小飞鱼工作流在线培训教程文件夹及意见征集

    最近通达OA技术交流群有不少朋友反映说表单设计这块 改动样式的问题,这块须要html和css的改动.本来最近正好要在工作流这块准备做一个系列的课程,都是基础的设置主要是给刚接触工作流的朋友用的,大家有 ...

  9. Android UI开发第三十三篇——Navigation Drawer For Android API 7

    Creating a Navigation Drawer中使用的Navigation Drawer的android:minSdkVersion="14",现在Android API ...

  10. PetShop 4.0学习笔记:消息队列MSMQ

    直到今天才知道,在我们每天都在用的Window系统里还有这么好用的一个编程组件:消息队列.它能够解决在大数据量交换的情况下的性能问题,特别是BS系统的数据库性能.而且它的异步处理方式能给程序员最大的便 ...