【精】用戶端(iOS、Android)/Server,APP內部的通訊協定,跨平台方案

來源:互聯網
上載者:User

【僅用於技術交流,未經允許禁止轉載】
        通常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 用來指定用戶端做出的響應 string 1.1
url 需跳轉的網址 string www.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的關鍵代碼:

//向CPMParse註冊的回呼函數。- (void)onCPMIsComing:(NSNotification *)noti{    NSDictionary *userInfo = noti.userInfo;    if ([userInfo isKindOfClass:[NSDictionary class]]) {        CPMCommand *command = [userInfo objectForKey:kCPMessageParserNotificaitonCommandKey];//        if ([command isKindOfClass:[CPMCommand class]]) {            [self handleCPM:command];        }    }}//真正處理CPMessage的地方。通過NSInvocation將message命令和UIViewController關聯起來- (void)handleCPM:(CPMCommand *)message{    [self prepareProcessors];    CPMItem *item = (CPMItem *)[self.processors objectForKey:NSStringFromCPMessageCommand(message)];    if (nil == item) {        return;    }    [item getInfoFromCPMessageCommand:message];        UIViewController *visibleCtrl = [self.assit topViewController];    Class receiverClass = NSClassFromString(item.className);        if (item.classMethod != nil) {        [self performClassSelector:item.classMethod onTarget:receiverClass withArgs:item.classArgs];        [item clearInfoFromCPMessageCommand];    } else if (!item.isOpenAsNew && [visibleCtrl isMemberOfClass:receiverClass]) { // if target viewcontroller is top viewcontroller        if (item.reloadMethod != nil) {            [self performSelector:item.reloadMethod onTarget:visibleCtrl withArgs:item.initialArgs];        } else if (item.optMethod != nil) {            [self performSelector:item.optMethod onTarget:visibleCtrl withArgs:item.optArgs];        } else {            // no reloadSEL and no optSEL then do nothing        }        [item clearInfoFromCPMessageCommand];    } else {        UIViewController *baseCtl = [item.processor perpareOpeningWithHelpFrom:self.assit];        //必須要dispatch_async,否則不會回調viewWillDisappear和viewDidDisappear        dispatch_async(dispatch_get_main_queue(), ^{            // if target viewcontroller is top viewcontroller            if (!item.isOpenAsNew && [baseCtl isMemberOfClass:receiverClass]) {                if (item.reloadMethod != nil) {                    [self performSelector:item.reloadMethod onTarget:baseCtl withArgs:item.initialArgs];                } else if (item.optMethod != nil) {                    [self performSelector:item.optMethod onTarget:baseCtl withArgs:item.optArgs];                } else {                    // no reloadSEL and no optSEL then do nothing                }            } else {                id receiverSuper = [receiverClass alloc];                id receiver = [self performSelector:item.initialMethod onTarget:receiverSuper withArgs:item.initialArgs];                if (nil == receiver || ![receiver isKindOfClass:[UIViewController class]]) {                    [receiverSuper release];                } else {                    [item.processor doOpen:receiver on:baseCtl];                    [receiver release];                }            }            [item clearInfoFromCPMessageCommand];        });    }}/*  NSInvocation 施展才華的地方,selector和args 調用方法。*/- (id)performSelector:(SEL)aSelector onTarget:(id)target withArgs:(NSArray *)args{    id ret = nil;        if (aSelector == nil || target == nil || ![target respondsToSelector:aSelector]) {        return ret;    }        NSMethodSignature *signature = [target methodSignatureForSelector:aSelector];    if (args.count + 2 != signature.numberOfArguments) {        return ret;    }        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];    [invocation setSelector:aSelector];    [invocation setTarget:target];    for (int i = 2, j = 0; j < args.count; i++, j++) {        id arg = [args objectAtIndex:j];        [invocation setArgument:&arg atIndex:i];    }    [invocation invoke];        NSUInteger length = [[invocation methodSignature] methodReturnLength];    //    void *buffer = (void *)malloc(length);    if (length > 0) {        [invocation getReturnValue:&ret];    }        return ret;}- (id)performClassSelector:(SEL)aSelector onTarget:(Class)target withArgs:(NSArray *)args{    id ret = nil;        if (aSelector == nil || target == nil) {        return ret;    }        NSMutableString *argsInCTypes = [NSMutableString stringWithString:@"v"];    for (int i = 0; i < args.count; ++i) {        [argsInCTypes appendString:@"@:"];    }    NSMethodSignature *signature = [target methodSignatureForSelector:aSelector]; // [NSMethodSignature signatureWithObjCTypes:argsInCTypes.UTF8String];    if (signature == nil || args.count + 2 != signature.numberOfArguments) {        return ret;    }        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];    [invocation setSelector:aSelector];    [invocation setTarget:target];    for (int i = 2, j = 0; j < args.count; i++, j++) {        id arg = [args objectAtIndex:j];        [invocation setArgument:&arg atIndex:i];    }    [invocation invoke];        NSUInteger length = [[invocation methodSignature] methodReturnLength];    //    void *buffer = (void *)malloc(length);    if (length > 0) {        [invocation getReturnValue:&ret];    }        return ret;}//將UIViewController 的類名、selector分離出來,存到processor中。- (void)prepareProcessors{    NSMutableDictionary *processors = [NSMutableDictionary new];    CPMType type = -1;    CPMItem *item = nil;    //////////////////////////////////////////////////////////////////////////    type = CPMessageTypeSingleHTML5;    item = [CPMItem CPMessageItemWithProcessor:[CPMessageProcessors CPMessageProcessorForMessage:type]                                     className:@"WebViewController"                                   classMethod:nil                                    initMethod:@selector(initWithsURL:title:)                                  reloadMethod:@selector(loadWithsURL:title:)                                     optMethod:nil];    if (IS_VALID_CPMessageTYPE(type) && (nil != item)) {        [processors setObject:item forKey:CPMessageKeys[type]];        item = nil;    }//添加其他item    self.processors = processors;    [processors release];}



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.