【精】客户端(iOS、Android)/Server,APP内部的通信协议,跨平台方案

原文链接:http://blog.csdn.net/hherima/article/details/50827363

【仅用于技术交流,未经允许禁止转载】
        通常APP-Server使用http协议,告诉APP需要的展示内容图片,文字。这是一种最为常见的协议了。另外一种客户端内协议,比如新闻APP,点击一个焦点新闻,APP跳到相应的频道或专题。不难发现:前者是Server->APP通信,主要是内容类型的,后者是APP<—>APP内通信,主要是命令、动作类型的。
        如何更好的处理APP内通信,这里有必要先介绍一下iOS平台进程间通信。

进程间通信

        以iOS平台为例,进程间通信IPC,可以通过自定义URL Schema,然后APP实现-(BOOL) application:(UIApplication *) openURL:(NSURL *) sourceApplication:(NSString *) annotation:(id) 方法,可以处理一些命令和参数。其中sourceApplication即调用者的bundleIdentifier。比如下面这条URL,会调起twitter客户端,并拉起发送推文的界面。


        URL Scheme完美解决了同一个操作系统中,调用APP和传输消息和命令的问题。URL Schema机制,给开发者提供了很好的思路,可以解决APP内通信问题。CPMessage应运而生。

什么是CPMessage 

        CPMessage(Cross Platform Message跨平台消息)可以当做服务器/客户端(让客户端处理一些命令),或者客户端内部通信的桥梁。通信的发起者可能是服务器、Push、用户操作、HTML5或者是别的APP。客户端根据具体命令和相应参数,执行相应动作。总之,CPMessage思路源于iOS平台的URL Schema机制。

CPMessage的机制

        为了确保多平台的一致性,使用协议名称CPM作为开头,只是传递协议的方式不同。在iOS上,向客户端通信的方式是scheme+protocol,例如:
cpm://message.cmd?messageID=MsgID&urls=EncodedURLStrings&uid=uid&ex1=ex1&ex2=ex2&ex3=ex3
        下面将各部分标注一下:

        一般来说:命令名称都是message.cmd,经常改变的是messageID(下面详细介绍)。参数列表中,messageID表明是那种动作,比如,打开一个webview、打开新的页面、还是调起登录框等。参数列表后面是一些辅助参数。下面的表格给出了一些示例。

参数用途格式示例
MessageID用来指定客户端做出的响应string1.1
url需跳转的网址stringwww.baidu.com
ex1额外参数string“03”

        另外,如果ex1、ex2和ex3不够用。还可以使用more参数。比如:more = {"title":"helloworld","name":"jack"};more参数格式为json格式。方便客户端解析字段。

MessageID

        MessageID根据可分为mainID和subID,比如MessageID=2.1。那么mainID是2,subID是1。一般来说mainID是操作类型的区分,比如:
            mainID=1表示打开页面。
            mainID=2是传递数据。
            mainID=3 是打开其他sdk等等。
        subID则是一些操作的细分类型,比如:打开那个页面等等。
        APP都是跨平台的,至少有iPhone,Android客户端,那么URL Schema头部将是不同的。举例:Server给的H页面中,某个链接是个CPM消息,URL Scheme配合CPM的使用,如下格式:

 myapp://cpm://xxxxxx。操作系统级别会读取到“myapp://”从而拉起客户端,客户端内部读取到“cpm://”

CPMessage的使用

        开发者如何应用CPMessage到自己的APP中呢?你需要做三件事:
         第一,提前注册一些UIViewController和方法,封装成一个个CPMessageItem。
         第二,当CPMessage过来的时候,将其解析成CPMessageCommand,其中CPMessageCommand包含了参数。
         第三,使用一个CPMManager类,将command 和 item对应到某一个UIViewController,并使用NSInvocation,调用之前注册好的方法。
        那么,回到刚才一开始的问题,我们假设MessageID = 1.1 即跳转频道,ex1即频道的index。那么,Server将下面的URL消息,封装到焦点新闻里
        cpm://message.cmd?messageID=1.1&ex1=03
        客户端TabViewController注册一个调转频道的method -(void) switchToChannel:(int)index,执行CPMManager,TabViewController 执行switchToChannel方法。可以很优雅的解决新闻APP,调转到任意频道的问题。

CPMManager的UML图+流程图


CPMManger 的几个关键函数
    prepareProcessor 事先注册好一些类和类方法
    handleUrl:接收url,带有cpm schema的url
    onCPMIsComing:参数是CPMCommand
    handleCPM:参数CPMCommand。最终通过[NSInvocation invoke]实现回调。

CPMManger的实现

这里只列出部分代码,即CPMManger的关键代码:

[objc]  view plain  copy
  1. //向CPMParse注册的回调函数。  
  2. - (void)onCPMIsComing:(NSNotification *)noti  
  3. {  
  4.     NSDictionary *userInfo = noti.userInfo;  
  5.     if ([userInfo isKindOfClass:[NSDictionary class]]) {  
  6.         CPMCommand *command = [userInfo objectForKey:kCPMessageParserNotificaitonCommandKey];//  
  7.         if ([command isKindOfClass:[CPMCommand class]]) {  
  8.             [self handleCPM:command];  
  9.         }  
  10.     }  
  11. }  
  12.   
  13. //真正处理CPMessage的地方。通过NSInvocation将message命令和UIViewController关联起来  
  14. - (void)handleCPM:(CPMCommand *)message  
  15. {  
  16.     [self prepareProcessors];  
  17.     CPMItem *item = (CPMItem *)[self.processors objectForKey:NSStringFromCPMessageCommand(message)];  
  18.     if (nil == item) {  
  19.         return;  
  20.     }  
  21.     [item getInfoFromCPMessageCommand:message];  
  22.       
  23.     UIViewController *visibleCtrl = [self.assit topViewController];  
  24.     Class receiverClass = NSClassFromString(item.className);  
  25.       
  26.     if (item.classMethod != nil) {  
  27.         [self performClassSelector:item.classMethod onTarget:receiverClass withArgs:item.classArgs];  
  28.         [item clearInfoFromCPMessageCommand];  
  29.     } else if (!item.isOpenAsNew && [visibleCtrl isMemberOfClass:receiverClass]) { // if target viewcontroller is top viewcontroller  
  30.         if (item.reloadMethod != nil) {  
  31.             [self performSelector:item.reloadMethod onTarget:visibleCtrl withArgs:item.initialArgs];  
  32.         } else if (item.optMethod != nil) {  
  33.             [self performSelector:item.optMethod onTarget:visibleCtrl withArgs:item.optArgs];  
  34.         } else {  
  35.             // no reloadSEL and no optSEL then do nothing  
  36.         }  
  37.         [item clearInfoFromCPMessageCommand];  
  38.     } else {  
  39.         UIViewController *baseCtl = [item.processor perpareOpeningWithHelpFrom:self.assit];  
  40.         //必须要dispatch_async,否则不会回调viewWillDisappear和viewDidDisappear  
  41.         dispatch_async(dispatch_get_main_queue(), ^{  
  42.             // if target viewcontroller is top viewcontroller  
  43.             if (!item.isOpenAsNew && [baseCtl isMemberOfClass:receiverClass]) {  
  44.                 if (item.reloadMethod != nil) {  
  45.                     [self performSelector:item.reloadMethod onTarget:baseCtl withArgs:item.initialArgs];  
  46.                 } else if (item.optMethod != nil) {  
  47.                     [self performSelector:item.optMethod onTarget:baseCtl withArgs:item.optArgs];  
  48.                 } else {  
  49.                     // no reloadSEL and no optSEL then do nothing  
  50.                 }  
  51.             } else {  
  52.                 id receiverSuper = [receiverClass alloc];  
  53.                 id receiver = [self performSelector:item.initialMethod onTarget:receiverSuper withArgs:item.initialArgs];  
  54.                 if (nil == receiver || ![receiver isKindOfClass:[UIViewController class]]) {  
  55.                     [receiverSuper release];  
  56.                 } else {  
  57.                     [item.processor doOpen:receiver on:baseCtl];  
  58.                     [receiver release];  
  59.                 }  
  60.             }  
  61.             [item clearInfoFromCPMessageCommand];  
  62.         });  
  63.     }  
  64. }  
  65. /* 
  66.   NSInvocation 施展才华的地方,selector和args 调用方法。 
  67. */  
  68. - (id)performSelector:(SEL)aSelector onTarget:(id)target withArgs:(NSArray *)args  
  69. {  
  70.     id ret = nil;  
  71.       
  72.     if (aSelector == nil || target == nil || ![target respondsToSelector:aSelector]) {  
  73.         return ret;  
  74.     }  
  75.       
  76.     NSMethodSignature *signature = [target methodSignatureForSelector:aSelector];  
  77.     if (args.count + 2 != signature.numberOfArguments) {  
  78.         return ret;  
  79.     }  
  80.       
  81.     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];  
  82.     [invocation setSelector:aSelector];  
  83.     [invocation setTarget:target];  
  84.     for (int i = 2, j = 0; j < args.count; i++, j++) {  
  85.         id arg = [args objectAtIndex:j];  
  86.         [invocation setArgument:&arg atIndex:i];  
  87.     }  
  88.     [invocation invoke];  
  89.       
  90.     NSUInteger length = [[invocation methodSignature] methodReturnLength];  
  91.     //    void *buffer = (void *)malloc(length);  
  92.     if (length > 0) {  
  93.         [invocation getReturnValue:&ret];  
  94.     }  
  95.       
  96.     return ret;  
  97. }  
  98.   
  99.   
  100. - (id)performClassSelector:(SEL)aSelector onTarget:(Class)target withArgs:(NSArray *)args  
  101. {  
  102.     id ret = nil;  
  103.       
  104.     if (aSelector == nil || target == nil) {  
  105.         return ret;  
  106.     }  
  107.       
  108.     NSMutableString *argsInCTypes = [NSMutableString stringWithString:@"v"];  
  109.     for (int i = 0; i < args.count; ++i) {  
  110.         [argsInCTypes appendString:@"@:"];  
  111.     }  
  112.     NSMethodSignature *signature = [target methodSignatureForSelector:aSelector]; // [NSMethodSignature signatureWithObjCTypes:argsInCTypes.UTF8String];  
  113.     if (signature == nil || args.count + 2 != signature.numberOfArguments) {  
  114.         return ret;  
  115.     }  
  116.       
  117.     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];  
  118.     [invocation setSelector:aSelector];  
  119.     [invocation setTarget:target];  
  120.     for (int i = 2, j = 0; j < args.count; i++, j++) {  
  121.         id arg = [args objectAtIndex:j];  
  122.         [invocation setArgument:&arg atIndex:i];  
  123.     }  
  124.     [invocation invoke];  
  125.       
  126.     NSUInteger length = [[invocation methodSignature] methodReturnLength];  
  127.     //    void *buffer = (void *)malloc(length);  
  128.     if (length > 0) {  
  129.         [invocation getReturnValue:&ret];  
  130.     }  
  131.       
  132.     return ret;  
  133. }  
  134.   
  135. //将UIViewController 的类名、selector分离出来,存到processor中。  
  136. - (void)prepareProcessors  
  137. {  
  138.     NSMutableDictionary *processors = [NSMutableDictionary new];  
  139.     CPMType type = -1;  
  140.     CPMItem *item = nil;  
  141.   
  142.     //  
  143.     type = CPMessageTypeSingleHTML5;  
  144.     item = [CPMItem CPMessageItemWithProcessor:[CPMessageProcessors CPMessageProcessorForMessage:type]  
  145.                                      className:@"WebViewController"  
  146.                                    classMethod:nil  
  147.                                     initMethod:@selector(initWithsURL:title:)  
  148.                                   reloadMethod:@selector(loadWithsURL:title:)  
  149.                                      optMethod:nil];  
  150.     if (IS_VALID_CPMessageTYPE(type) && (nil != item)) {  
  151.         [processors setObject:item forKey:CPMessageKeys[type]];  
  152.         item = nil;  
  153.     }  
  154.   
  155.   
  156. //添加其他item  
  157.   
  158.   
  159.     self.processors = processors;  
  160.     [processors release];  
  161. }  



版权声明:原创文章,未经博主允许禁止转载。欢迎点击头像上方“郭晓东的专栏”查看专栏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值