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, ), ^{
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-异步消息同步问题-典型使用场景: 微信私信界面的更多相关文章
- 异步消息队列Celery
Celery是异步消息队列, 可以在很多场景下进行灵活的应用.消息中包含了执行任务所需的的参数,用于启动任务执行, suoy所以消息队列也可以称作 在web应用开发中, 用户触发的某些事件需要较长事件 ...
- 循序渐进做项目系列(2):最简单的C/S程序——消息异步调用与消息同步调用
上篇博客 循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法 实现了一个最简单的C/S程序,即让服务器来做加法.当时为了通俗易懂采用了消息异步调用的方式.今天我们要采用消息同步调用的方式 ...
- ZooKeeper典型应用场景
ZooKeeper典型应用场景一览 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新.例 ...
- ZooKeeper典型应用场景一览
原文地址:http://jm-blog.aliapp.com/?p=1232 ZooKeeper典型应用场景一览 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据 ...
- ZooKeeper典型应用场景(转)
ZooKeeper是一个高可用的分布式数据管理与系统协调框架.基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题.网上 ...
- ZooKeeper典型应用场景概览
ZooKeeper是一个高可用的分布式数据管理与系统协调框架.基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题.网上 ...
- kafka系列八、kafka消息重复和丢失的场景及解决方案分析
消息重复和丢失是kafka中很常见的问题,主要发生在以下三个阶段: 生产者阶段 broke阶段 消费者阶段 一.生产者阶段重复场景 1.根本原因 生产发送的消息没有收到正确的broke响应,导致pro ...
- <zk的典型应用场景>
Overview zk是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布与订阅. 另一方面,通过对zk中丰富的数据节点进行交叉使用,配合watcher事件通 ...
- 搞懂分布式技术6:Zookeeper典型应用场景及实践
搞懂分布式技术6:Zookeeper典型应用场景及实践 一.ZooKeeper典型应用场景实践 ZooKeeper是一个高可用的分布式数据管理与系统协调框架.基于对Paxos算法的实现,使该框架保证了 ...
随机推荐
- 【原】spark-submit提交应用程序的内部流程
我们经常通过spark-submit来提交spark应用程序,那么让我们一起看一下这里面到底发生了什么吧. 知识点: 1.CLI命令行界面启动Spark应用程序 Unix有两种方式:1)spark-s ...
- [有错误]堆排序的实现 java
#include<iostream> using namespace std; //大根堆,从小到达排序 ]; void swap(int &a,int &b) { a=a ...
- 洛谷P1220 关路灯
洛谷1220 关路灯 题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关 ...
- Oracle10g/11g 在SUSE/RHEL上的安装与配置
在过去对众多项目的支撑过程中,Oracle作为首选数据库,其安装与配置过程成了重复性最多的工作之一.在此,我进行了总结,并分享出来,希望能对大家有所帮助.随着Oracle版本的提升,从9i -> ...
- .NET Compact Framework Data Provider for SQL Server CE
.NET Compact Framework Data Provider for SQL Server Mobile Standard Data Source=MyData.sdf;Persist S ...
- SIP SDP RTSP RTP RTCP webrtc
rfc1889 rfc2326 rfc3261 rfc3550 rfc3856 rfc6120. SIP SDP RTSP RTP RTCP,就像他们出现的顺序一样,他们在实际应用中的启用 ...
- storm出现的背景
storm出现的背景 互联网从诞生的第一时间起,对世界的最大改变就是让信息能够实时交互,从而大大加速了各个环节的效率.正因为大家有对信息实时响应.实时交互的需求,所以软件行业除了个人操作系统之外,数 ...
- AJAX中文乱码PHP完美解决(IE和Firefox兼容)
最近在做一个项目,遇到AJAX中文乱码问题,经过一个下午的努力终于完美解决,现将心得写下来,希望对那些还困绕在这个问题而头痛不已的人们有所帮助. 众所周知,使用AJAX传送和接收中文参数时,如果不在客 ...
- 用Windbg来看看CLR的JIT是什么时候发生的
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:用Windbg来看看CLR的JIT是什么时候发生的.
- IOS开发之tableview只选中一行
场景:一个弹出层,包含一个Tableview,每一行为一个选择条件,且只能选择一个.选中后文体有颜色变化,后面还会有对勾.选择另一个后,前一个恢复成普通状态. 示例代码: -(void)tableVi ...