iOS程序设计心得总结(二)网络层设计
来源:互联网 发布:淘宝店铺四个钻石 编辑:程序博客网 时间:2024/05/22 03:23
网络层设计
在做网络层设计时,我一般把设计的重心主要集中在请求、连接的统一管理、参数及回调的统一控制、连接层的分离,以及网络层的"尽力而为"设计上,这些都是在网络层的内部去做一些设计。最终的目的,想要达到是竭尽全力的减轻上层(应用层)负担,尽可能或是完全的做到对上层的完全透明与分离,对上层暴露统一的接口,上层仅仅作为调用(使用)者的身份,完全不用关心网络层的连接、以及相关业务、数据的处理,它就关心自己负责的界面交互及业务串联就好了。
网络层主要有两个职责:1.数据请求,与服务器建立数据通道;2.数据获得后,进行数据处理,然后存储,反馈。下面,两个职责分开来说
一.数据请求
1.基本设计
首先,我们可以对网络层细分为业务层和连接层
与服务器打交道一般也就是采用http请求或socket连接,这个层面即是我前面所谓的连接层。这个层面的封装实现其实别人都为我们做好了,ASIHttpRequest、AFNetworking、GCDAsyncSocket这些著名的第三方库就是对这个层面的封装,我们直接拿来使用即可,我们要考虑的是这些库在使用层面上的设计(网络业务层)。下面,是我一般在项目中对网络层的组织与实现(dataproxy这个名字可能有歧义,起的不好,忽略,反正这些类就是负责网络层工作的)
主要看JHDataProxy这个类,其它的类就是将接口按业务大类划分到不同的类别(Category)文件中去实现,避免JHDataProxy文件过于臃肿
@interface JHDataProxy : NSObject{ AFHTTPSessionManager *_manager;//公用一个AFHTTPSessionManager,复用http连接 AFHTTPSessionManager *_fileServerManager; AFHTTPSessionManager *_imManager;}-(void)cancleAllRequest;-(void)setSystemParams:(NSMutableDictionary *)paramDict;-(BOOL)responseIsSuccess:(id)responseObject;
-(id)init{ self = [super init]; if (self) { _manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:ServerAdd]]; _manager.requestSerializer = [AFJSONRequestSerializer serializer]; _manager.requestSerializer.timeoutInterval = 15; [_manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer]; responseSerializer.removesKeysWithNullValues = YES; responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain",@"application/json", nil]; _manager.responseSerializer = responseSerializer; ......... //_fileServerManager 配置 //_imManager 配置 ............ } return self;}-(void)cancleAllRequest{ //取消所有的请求}//统一设置一些系统级参数-(void)setSystemParams:(NSMutableDictionary *)paramDict{ [paramDict setObject:APP_CLIENT_ID forKey:@"clientId"]; [paramDict setObject:[JHPhoneManager shareJHPhoneManager].appUser.workId forKey:@"userName"]; NSInteger currentTime = [[NSDate date] timeIntervalSince1970]; [paramDict setObject:[NSNumber numberWithInteger:currentTime] forKey:@"timestamp"]; [paramDict setObject:[NSNumber numberWithInteger:[JHPhoneManager shareJHPhoneManager].appUser.companyId] forKey:@"companyId"]; ............. return;}//回调里公共参数的统一处理-(BOOL)responseIsSuccess:(id)responseObject{ if (responseObject) { int errorCode = [[responseObject objectForKey:@"errorCode"] intValue]; if (errorCode == 0) { return YES; } else { if (errorCode == 4001 || errorCode == 4002) { [[JHPhoneManager shareJHPhoneManager] clearAllForLogout]; ............. } return NO; } } else return NO;}通过JHDataProxy类的实现,我可以达到以下目的,1.网络(连接)资源的共享复用,多个请求可以共用一个HttpSessionManager对象(好处可以看看关于Afnetworking的使用及底层原理,其它框架也有类似机制);2.对HttpSessionManager的统一配置,服务器baseurl、cache策略、超时时间、数据请求返回格式的设置等等,都统一、集中到了JHDataProxy类中;3.请求控制的集中管理,比如统一的cancleRequest操作等;4.通用基础参数的控制,setSystemParams集中控制了系统级参数的统一添加;5.接口返回的统一处理,比如处理所有接口的response errorCode、解析返回中的sessionId等等。这些基础性的通用设置、操作和处理,都放到了一处集中处理,不用分散到每次具体的请求与回调当中,达到了项目内网络层的重用,同时便用管理和设置。
2.设计改进
FEIM_Network即是对连接层的抽离,CUHDataProxy则是网络业务层的实现,CUHDataProxy会对FEIM_Network进行调用,比如CUHDataProxy中的这个实现
-(void)getDiseaseList:(NSInteger)departmentId{ [_netWork getDiseaseList:departmentId];//_netWork是FEIM_NetWork对象}它完全不用关心FEIM_Network是用什么方式与服务器打交道的,调用FEIM_Network的getDisraseList方法后,它只要实现FEIM_Network的请求回调,处理回调即可了,FEIM_Network可能用的是ASIHttpRequest也可能是AFNetWorking,甚至也可能换成Socket,都无所谓,连接层的修改与实现都在FEIM_Network中去做就行了,不会对CUHDataProxy造成任何影响。(PS:FEIM_Network还可以进一步通过设计做到与业务无关,不过目前我还没有在项目中做到过这个层次的进一步分离,没有这个强需求).
二.尽力而为--回调数据的处理
-(void)getOfflineMsg:(void(^)())success fail:(void(^)())fail{ NSMutableDictionary *paramDict = [[NSMutableDictionary alloc] init]; [self setSystemParams:paramDict]; double lastedReceiveMsgTime = [JHBaseMessage getLastedReceiveMsgTime]; [paramDict setObject:[NSNumber numberWithInteger:lastedReceiveMsgTime*1000] forKey:@"lastMessageTime"]; [_imManager POST:GetOfflineMsg parameters:paramDict progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { if ([self responseIsSuccess:responseObject]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //群离线消息 ........... //个人离线消息 ....... //离线serverMsg消息 ......... }]; //保存离线消息,排重,计算未读消息数目,生成最近消息对象,按人(群)生成排重后的离线消息数组 [JHBaseMessage saveOfflineMsgs:allSingleMsgs roomMsgs:allRoomsMsgs result:^(NSMutableArray *recentMsgs, NSMutableDictionary *distinctMsgsDict) { [JHRecentMessageRecord saveOrUpdateRecentMsgs:recentMsgs needForceSetUnReadNum:NO]; ............... dispatch_async(dispatch_get_main_queue(), ^{ ........... success(); }); }]; }); } else { ......... fail(); } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { if(error.code == -999) return; else { ......... fail(); } }];}离线消息取得后,我需要解析出群离线消息、个人离线消息、服务器离线消息、对消息进行排重、计算未读消息数、消息本地存储等等,这一系列操作都是统一的,无论我在何时何处去获取离线消息都要进行这些处理,那我何不把这些处理都封装到网络层中呢,这样上层任何地方只要简简单单的调用[_dataProxy getOfflineMsg]一句话后,后续一系列的数据业务处理、数据存储等等它都不用去操心了,最终它只要在回调里完成它上层界面反馈等上层业务即可。
-(void)getBasicUserInfo:(NSString *)rqUserId success:(void(^)(JHCorpContact *contact))success fail:(void(^)())fail{ NSMutableDictionary *paramDict = [[NSMutableDictionary alloc] init]; [self setSystemParams:paramDict]; [paramDict setObject:rqUserId forKey:@"rq_userName"]; [_manager POST:GetBasicUserInfo parameters:paramDict progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { if ([self responseIsSuccess:responseObject]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //数据解析 NSDictionary *userDict = [responseObject objectForKey:@"lightUser"]; JHCorpContact *contact = [[JHCorpContact alloc] init]; contact.contactType = CORP_CONTACT; contact.contactId = [userDict objectForKey:@"userName"]; contact.name = [userDict objectForKey:@"name"]; contact.sex = [[userDict objectForKey:@"gender"] intValue]; ......... //数据存储 [JHCorpContact saveOrUpdateCorpContactBasicInfo:contact]; //返回模型 dispatch_async(dispatch_get_main_queue(), ^{ success(contact); }); }); } else { fail(); } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { if(error.code == -999) return; else fail(); }];}同样,无论何时何处我调用获取用户详情接口,我要做的都是解析服务器返回的JSON数据转化成用户模型、把用户的详情更新到本地存储,这些处理都是一致的。上层调用时拿到的就是一个返回的数据模型,它唯一要关心的就是这个模型在上层展示、反馈或交由下一个业务即可。
- (void)sendMsgTo:(NSString *)friendUserId content:(NSString *)msgContent finish:(void(^)(JHMessageObject *msg))finish{ //创建消息相关模型 JHMessageObject *msg = [[JHMessageObject alloc] init]; msg.msgDate = [self getCurrentTime]; msg.msgUniqueID = [JH_Util uniqueString]; msg.msgContent = msgContent; ....... ......... //创建xmpp消息 XMPPMessage *mes=[XMPPMessage messageWithType:@"chat" to:[XMPPJID jidWithUser:friendUserId domain:JHIM_DOMAIN resource:IMSOURCE] elementID:msg.msgUniqueID]; [mes addChild:[DDXMLNode elementWithName:@"body" stringValue:msgContent]]; [mes addChild:[self buildTextMsgExtendField:msg]]; //发送消息 if ([self.xmppStream isAuthenticated]) { msg.msgStatus = MsgSending; //发送消息 [self sendXMPPMessage:mes]; } else { msg.msgStatus = MsgFail; } //消息存储 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [JHMessageObject saveSingleMsg:msg]; [JHRecentMessageRecord saveOrUpdateRecentMsg:recentMsg needForceSetUnReadNum:YES]; }); finish(msg);}举第三个例子,想要说明的是这种设计的另一个好处。这里利用xmppstream与openfire服务器打交道(有来有回,其实就是socket),同样,无论何时何处去做消息发送,都是这一套业务,建立模型->发送->存储模型->回调(返回模型),这种统一的业务处理,都应按照上面的设计思路封装起来,上层只需要单纯的关心何时去触发发送动作,动作完成后如何反馈或进行下一步业务即可。前不久,公司准备把我们的IM基础通讯部分单独提出来,封装成SDK,按照这种设计实现的代码,我在做SDK接口封装时,基本上没有做任何额外的工作,直接把网络层、数据层文件从项目中拿出来后,简单整理修改了一下,SDK就出来了(我看最终的SDK和环信的接口定义、设计思路基本都是一致的,SDK负责的就是收发消息(网络层)+本地消息存取(数据层)的工作),通过设计可以很好的将下层(数据层+网络层)与上层(应用层)进行分离,下层代码具有高度的独立性与可(复)用性。
- iOS程序设计心得总结(二)网络层设计
- iOS程序设计心得总结(一)前言与数据层设计
- iOS程序设计心得总结(三)应用层设计
- iOS网络层设计感想
- iOS网络层设计感想
- iOS网络层设计感想
- iOS网络层设计感想
- iOS网络层架构设计分享
- iOS网络层架构设计1
- iOS网络层架构设计分享
- iOS路由设计(二)移动端路由层设计
- 心得:android开发网络层
- C#/WPF 程序设计心得(二)
- 网络五子棋的架构设计(二)--界面层
- IOS开发 - 网络总结(二)
- iOS 网络层文档
- IOS开发心得总结
- Android网络层与数据层设计
- SSH远程拷贝文件
- 存储过程详解
- Linux下的Memcache安装及安装Memcache的PHP扩展安装
- 数据中心网络里的Underlay和Overlay
- VisualVM远程连接并监控服务器上的jvm进程
- iOS程序设计心得总结(二)网络层设计
- iOS build 与version,InfoDictionary version的区别
- Android 热修复框架 Tinker ( 三 )
- easyUI的不同部门查看不同的信息sql语句和总结
- Jsp内置对象
- Linux系统中各种系统日志文件主要存放在系统中哪个目录
- java的switch语句问题
- redis高可用集群介绍
- 一键打包出多个不同包名,不同应用名称和图标的APK