<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文由有贊技術團隊原創分享,原題“有贊 APP IM SDK 組件架構設計”,即時通訊網收錄時有修訂和改動,感謝原作者的無私分享。

    1、引言

    本文主要以Android客戶端為例,記錄了有贊旗下 App 中使用自研 IM,并將IM提煉成組件化SDK的設計思路。此項工作由有贊移動開發組 IM SDK 團隊共同討論完成。

     

    在有贊產品中,存在大量需要交易雙方溝通交流的場景,比如,客戶咨詢商家產品信息,售前售后簡單的答疑和維權等。另外,有贊業務還存在一些特殊的復雜場景,如供應商、分銷商、客戶三方之間需要同步溝通,會同時存在多種溝通角色。

    此時需要較為完善的即時通信(IM)解決方案,但是由于有贊針對不同的商戶和使用場景有多個APP,APP自行實現IM功能代價較大,且維護起來人力分散,于是,IM SDK項目便應運而生了,APP 通過接入此給件化SDK,可以快速實現IM基本功能。

    學習交流:

    - 即時通訊/推送技術開發交流5群:215477170[推薦]

    - 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

    本文已同步發布于“即時通訊技術圈”公眾號,歡迎關注:

    ▲ 本文在公眾號上的鏈接是:https://mp.weixin.qq.com/s/ANp1kuj65Ww5RpABl2M9RQ,原文鏈接是:http://www.52im.net/thread-3088-1-1.html

    2、相關文章

    從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路

    從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結》(* 推薦

    從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐

    一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)

    從零到卓越:京東客服即時通訊系統的技術架構演進歷程

    一套原創分布式即時通訊(IM)系統理論架構方案

    蘑菇街即時通訊/IM服務器開發之架構選擇

    自已開發IM有那么難嗎?手把手教你自擼一個Andriod版簡易IM (有源碼)

    適合新手:從零開發一個IM服務端(基于Netty,有完整源碼)

    拿起鍵盤就是干:跟我一起徒手開發一套分布式IM系統

    3、設計目標

    本次IM組件化SDK的設計目標有以下幾點:

    • 1)IM 主流程穩定可用:消息傳輸具有高可靠性;
    • 2)UI 組件直接集成進入SDK,并支持可定制化;
    • 3)富媒體發送集成進入SDK,并可按需定制需要的富媒體類型;
    • 4)實現消息傳輸層SDK,與帶有UI的SDK的功能分離,業務調用方既可以使用消息傳輸SDK,處理消息,然后自行處理UI,也可以使用帶有UI組件的SDK,一步實現較為完備的IM功能。

    4、整體結構

    下圖中簡要描述了有贊客戶端中IM系統的基本結構 : 

    如上圖所示,各分層的職責分工如下:

    • 1)消息通道層:維護Socket長連接作為消息通道,消息收發流程主要在這一層中完成;
    • 2)持久化層:主要將消息存入數據庫中,富媒體文件存入文件緩存中,方便第二次展示消息時候,從本地加載,而不是網絡層獲取;
    • 3)邏輯處理層:完成各種消息相關的邏輯處理,如排序,富媒體文件的預處理等;
    • 4)UI顯示層:將數據在UI上進行呈現。

    5、設計要點1:Socket長連接的創建與維護

    IM SDK 所有數據收發流程,均通過Socket長連接完成,如何維護一個穩定Socket通道,是IM系統是否穩定的重要一環。 

    下面描述下Socket通道幾個重要的流程。

    1)創建流程(連接) :

    如圖上所示,當IM SDK初始化后,業務調用連接請求接口,會開始連接的創建過程,創建成功后,會完成鑒權操作,當創建和鑒權都完成后,會開啟消息收發線程,為了維持長連接,會有心跳機制,特別的,會開啟一個心跳輪詢線程。

    2)心跳機制 :

    心跳機制,是IM系統設計中的常見概念,簡單的解釋就是每隔若干時間發送一個固定信息給服務端,服務端收到后及時回復一個固定信息,如果服務端若干時間內沒有收到客戶端心跳信息則視客戶端斷開,同理如果客戶端若干時間沒有收到服務端心跳回值則視服務端斷開。 

     

    當長連接創建成功后,會開啟一個輪詢線程,每隔一段時間發送心跳消息給服務器端,以維持長連接。

    有關IM心跳方面的專項文章,請見:

    手把手教你用Netty實現網絡通信程序的心跳機制、斷線重連機制

    為何基于TCP協議的移動端IM仍然需要心跳保活機制?

    移動端IM實踐:實現Android版微信的智能心跳機制

    移動端IM實踐:WhatsApp、Line、微信的心跳策略分析

    一文讀懂即時通訊應用中的網絡心跳包機制:作用、原理、實現思路等

    正確理解IM長連接的心跳及重連機制,并動手實現(有完整IM源碼)

    一種Android端IM智能心跳算法的設計與實現探討(含樣例代碼)

    手把手教你用Netty實現網絡通信程序的心跳機制、斷線重連機制

    3)重連流程 :

    重連被觸發時,如果該次連接成功,退出重連。反之重連失敗后,會判斷當前重連的次數是否超過預期值(這里設為6次),并對重連次數計數,如果超過就會退出重連,反之休眠預設的時間后再次進行重連操作。

    重連觸發條件分為三種:

    • a. 主動連接不成功(主動連接Socket,如果連接失敗,會觸發重連機制);
    • b. 網絡被主動斷開(正常建立連接,操作過程中,網絡被斷開,通過系統廣播觸發重連);
    • c. 服務器沒響應,心跳沒回值(服務端心跳預設時間內沒回值,客戶端認為服務端已經斷開,觸發重連)。

    有關重連機制的深入學習,可以閱讀以下兩篇:

    4)網絡狀態判斷:

    TCP API并沒有提供一個可靠的方法判斷當前長連接通道狀態,isConnected()和isClosed()僅僅告訴你當前的Socket狀態,不是是長連接斷開是一回事。 isConnected()告訴你是否Socket與Romote host保持連接,isClosed()告訴你是否Socket被關閉。 

    假如你判斷長連接通道是否被關閉,只能通過和流操作相關的以下方法:

    • a. read() return -1;
    • b. readLine() return null;
    • c. readXXX() throw EOPException for any other XXX;
    • d. write 將拋出IOException: Broken pipe(通道被關閉)。

    所以SDK封裝isConnected(方法的時候,是根據這幾種情況綜合判斷當前的通道狀態,而不是僅僅通過Socket.isConnected()或者Socket.isClosed()。

    6、設計要點2:消息發送流程

    消息發送流程主要有兩大類:

    1)一類是IM相關數據的請求,例如:歷史消息列表,會話列表等;

    2)另一類是IM消息的發送,主要是文字消息。

    富媒體消息發送,會將富媒體文件先上傳服務器后,拿到文件URL, 通過文字消息,將此URL發給接收方,接收方下載后進行UI展示)。 

    以上兩類消息發送,均使用上圖的流程進行發送,可通過發送回調感知請求的結果。

    如上圖所示,消息發送流程,需要先封裝消息請求,在通過發送隊列發送至服務器,發送前,在將請求id和對應回調存入本地Map數據結構中。

    if(requestCallBack != null) { 

      mCallBackMap.put(requestId, requestCallBack);

    }

    之后接收服務器推送消息(此消息帶有發送請求時的請求id),在本地的Map數據找到請求id對應的回調,然后通過回調返回服務器推送過來的數據。 

    請求可以通過泛型指定返回值類型,SDK中會自行解析服務器數據返回的數據,直接返回給業務調用方model對象,方便使用。(目前支持json格式的數據解析)

    private void IMResponseOnSuccess(String requestid, String response) { 

            if(mCallBackMap != null) {

               IMCallBack callBack = mCallBackMap.get(requestid);

               if(callBack == null) {

                   return;

               }

               if(callBack instanceofJsonResultCallback) {

                   finalJsonResultCallback resultCallback = (JsonResultCallback) callBack;

                   if(resultCallback.mType == String.class) {

                       callBack.onResponse(response);

                   } else{

                       Object object = newGson().fromJson(response, resultCallback.mType);

                       callBack.onResponse(object);

                   }

                   removeCallBack(requestid);

               }

            }

    }

    如下的示例中,展示了一個獲取會話列表的請求,可以看出目前的請求封裝,和一些第三方的的網絡庫類似,使用起來較為方便。

    RequestApi requestApi = new RequestApi(IMConstant.REQ_TYPE_GET_CONVERSATION_LIST, Enums Manager.IMType.IM_TYPE_WSC.getRequestChannel());

     

    requestApi.addRequestParams("limit", 100); 

    requestApi.addRequestParams("offset", 0);

     

    IMEngine.getInstance().request(requestApi, newJsonResultCallback<List<ConversationEntity>>() { 

        @Override

        publicvoidonResponse(List<ConversationEntity> response) {

            mSwipeRefreshLayout.setRefreshing(false);

            mAdapter.mDataset.clear();

            mAdapter.mDataset.addAll(response);

            mAdapter.notifyDataSetChanged();

        }

     

        @Override

        publicvoidonError(intstatusCode) {

            //do something

        }

    });

    可以看出,該請求直接返回了一個會話類型的List集合,業務方可直接使用。

    7、設計要點3:消息接收流程

    消息的監聽流程主要使用了一個全局監聽的方式來進行,需要先注冊監聽器,監聽器中有默認的回調。

    public interface IMListener { 

        /**

         * 連接成功

         */

        void connectSuccess();

     

        /**

         * 連接失敗

         */

        void connectFailure(EnumsManager.DisconnectType type);

     

        /**

         * 鑒權成功

         */

        void authorSuccess();

     

        /**

         * 鑒權失敗

         */

        void authorFailure();

     

        /**

         * 接收數據成功

         */

        void receiveSuccess(int reqType, String msgId, String requestChannel, String message, int statusCode);

     

        /**

         * 接收數據失敗

         */

        void receiveError(int reqType, String msgId, String requestChannel, int statusCode);

    }

    該監聽器中可以接收如下類型的消息:

    • 1)Socket連接狀態的返回結果;
    • 2)鑒權狀態的返回結果,(鑒權流程因有贊業務需要);
    • 3)接收的IM消息,或者其他類型的返回消息。可根據消息類型進行后續的分發處理。

    業務如需使用此全局監聽器,需要自行實現此接口,并在業務初始化時,注冊此監聽器即可。SDK中會根據注冊的監聽器,在讀取到服務器推送消息后,直接通過監聽器到回調進行分發。

    private void distributeData(IMEntity imEntity) { 

            if(mIMListener != null&& imEntity != null) {

           // 省略部分邏輯代碼

           ……

           if(status == Response.SUCCESS) {

               switch(responseModel.reqType) {

                   caseIMConstant.REQ_TYPE_AUTH: // 鑒權成功

                       mIMListener.authorSuccess();

                       return;

     

                   caseIMConstant.REQ_TYPE_OFFLINE: //  服務端踢客戶端下線

                       mIMListener.connectFailure(EnumsManager.DisconnectType.SERVER);

                       break;

     

                   caseIMConstant.REQ_TYPE_HEARTBEAT: // 心跳成功

                   caseIMConstant.REQ_TYPE_RECEIVER_MSG: // 收到回調消息

                       handleMessageID(responseModel.body);

                       break;

                   default:

                       break;

               }

               mIMListener.receiveSuccess(responseModel.reqType, msgId, responseModel

                       .requestChannel, responseModel.body, 0);

           } else{

               mIMListener.receiveError(responseModel.reqType, msgId, responseModel

                       .requestChannel, status);

           }

       }

    }

    部分接收消息,如心跳,多端登錄時被踢下線通知等,sdk內部會自行處理,業務基本無感知。

    8、設計要點4:可定制化的UI

    隨著公司規模的擴大與業務線的快速迭代,可能新的業務也需要 IM 這個功能,眾所周知,IM UI 功能的嵌入會占據大量的開發與調試時間, 為了解決這個痛點,決定將 IM UI 部分抽成一個 Library,實現可定制與單獨維護,做到真正的敏捷開發與快速迭代。

    8.1 UIKit設計 

    IM UIKit暴露相應的api接口,業務方注入相應的功能定制項,針對UI的點擊回調通過EventBus總線post分發,減少了業務方與UIKit的耦合,底層業務方通過MVP模式對View與Model進行解耦。

    定制項一般通過如下幾種方式。

    1)XML(定制業務信息,資源信息,顯示條數,各個業務功能開關等):

    <?xml version="1.0" encoding="utf-8"?>

    <resources>

        <stylename="limit">

            <!--每屏展示的條數-->

            <itemname="swiplimit">5</item>

            ......

        </style>

        ......

        ......

        <stylename="itembox">

            <itemname="showvoice">true</item>

            ......

            ......

            <itemname="more"show="true">

                <more>

                    <iconstyle="mipmap">im_plus_image</icon>

                    <itemname>測試</itemname>

                    <callback>false</callback>

                </more>

                 ......

                 ......

                <more>

                    <iconstyle="mipmap">ic_launcher</icon>

                    <itemname>測試</itemname>

                    <callback>true</callback>

                </more>

            </item>

            ......

            ......

        </style>

    </resources>

    2)Style(定制UI背景,氣泡顏色,字體大小等):

    <?xml version="1.0" encoding="utf-8"?>

    <resources>

        <!--im 聊天背景-->

        <stylename="imui_background">

            <itemname="android:background">@android:color/holo_red_dark</item>

        </style>

        ......

        ......

     

        <!--氣泡背景-->

        <stylename="bubble_background">

            <itemname="android:background">@mipmap/bubble_right_green</item>

        </style>

     

           <!--背景和和字段顏色定制-->

        <stylename="bg_and_textcolor"parent="bubble_background">

            <itemname="android:textColor">@android:color/holo_red_dark</item>

        </style>

        ......

        ......

    </resources>

    3)Model定制(傳入預設的定制Model模板填入相應參數,UIKit里面做相應解析):

    public class Entity {

        publicString action1;

        publicString action2;

        publicString aciton3;

        ......

    }

    8.2 UIKit 支持的富媒體類型

    除了文字消息之外,現在主流的IM系統中也支持各種富媒體發送,在有贊IM SDK UIKit中,目前也支持幾種富媒體發送。 以下是發送流程圖和兩類常見富媒體消息簡介。

    • 1)語音消息:除了使用常見的錄制和解碼播放的技術之外。還利用了 AudioManager 中 requestAudioFocus,abandonAudioFocus 相關方法,實現了錄制和播放語音消息,如果有第三方播放音樂,會自動暫停,錄制和播放語音消息結束后,聲音會自動播放。
    • 2)圖片消息:通過七牛服務器設置了縮略圖,接收方收到消息后,會先下載縮略圖,當用戶再點擊進入圖片詳情頁時,會下載大圖,Andorid客戶端使用Picasso加載庫加載圖片,并做本地緩存。

    9、設計要點5:UI 中聊天會話數據加載策略

    參考業界主流的IM系統方案,用戶聊天時,需要將已經發送和接收到的聊天信息保存到本地,而不是每次都拉取歷史數據。以達到節約流量和無網絡狀態下也查看數據的效果。

    為此IM SDK持久化層的數據庫中,也實現了簡單存儲加載機制,下面描述典型的數據加載場景。

    1)IM會話首次請求數據流程:

    2)IM下拉獲取歷史數據流程: 

    3)IM單條消息發送持久化方案:

    4)IM單條數據重發流程: 

    10、設計不足之處

    1)消息回執:

    當前的設計方案中,沒有消息回執的機制,也就是說接受方收到消息后,不會返回服務器收到消息的通知,服務器無法判斷消息是否推送成功,這樣在突然斷網,網絡模式切換,或者弱網環境下,會影響消息的到達率。 

    一種可行的設計方式是,發送方增加已送到和未送達的狀態,接收方收到消息后,給服務器返回已收到消息的通知,服務器再推送給發送方該狀態,如果沒有收到接收方回執,服務器可嘗試重新推送。發送方接受到接收方的收到回執后,更新發送狀態已發送,如果未收到,則顯示未送達。為了防止接收方回執丟失,接收方接收消息時候,可維護本地去重隊列。

    2)本地請求超時的判斷:

    本地發起的請求,沒有用定時器,完全依賴服務器返回或者出現Socket通道異常后上拋的通知作為超時判斷,部分場景可能覆蓋不到,需要對請求增加固定的超時處理機制,固定時候未收到請求,即認為超時。

    * 推薦學習:針對以上兩點不足,感興趣的讀者,可以研究一下MobileIMSDK開源工程源碼https://github.com/JackJiang2011/MobileIMSDK,MobileIMSDK已經實現了完整的消息送達保證機制(包括:ACK回執、重傳、去重、超時判定等等)。

    (本文同步發布于:http://www.52im.net/thread-3088-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 一级毛片人与动免费观看| 亚洲黄片毛片在线观看| 九九久久精品国产免费看小说| 97久久精品亚洲中文字幕无码 | 亚洲韩国在线一卡二卡| 亚洲国产精品丝袜在线观看| 毛片免费全部免费观看| 午夜视频免费在线观看| 又粗又硬又黄又爽的免费视频| 免费无码成人AV在线播放不卡| 亚洲五月综合网色九月色| 亚洲av永久无码精品表情包| 亚洲国产精品一区二区第四页 | 亚洲狠狠婷婷综合久久久久| 免费很黄很色裸乳在线观看| 四虎影院免费在线播放| av无码免费一区二区三区| 99久久免费观看| 日本高清免费观看| 中文无码日韩欧免费视频| 亚洲三级在线播放| 亚洲第一成年人网站| 久久精品国产亚洲| 亚洲人成网7777777国产| 久久精品夜色噜噜亚洲A∨| 亚洲欧洲中文日韩久久AV乱码 | 理论亚洲区美一区二区三区| 亚洲w码欧洲s码免费| 亚洲国产精品久久丫| 亚洲性色成人av天堂| 亚洲午夜精品一区二区公牛电影院| 77777_亚洲午夜久久多人| 亚洲好看的理论片电影| 亚洲狠狠综合久久| 亚洲成无码人在线观看| 亚洲国产视频一区| 男人天堂2018亚洲男人天堂| 亚洲中文字幕无码亚洲成A人片| 中文文字幕文字幕亚洲色| 亚洲综合av一区二区三区不卡| 亚洲成在人线aⅴ免费毛片|