基于Android平臺的RouterSDK設計與實現

MilSoper 7年前發布 | 9K 次閱讀 安卓開發 Android開發 移動開發

本文會詳細介紹了RouterSDK框架的設計與實踐,通過這篇文章不但可以知道Router框架的一些功能,而且還可以提供實現SDK的一些思路。

背景

在很多場景中,你可能會遇到以下這些需求:第三方AP啟動本AP、網頁調起AP、網頁啟動指定某個頁面、某個頁面需要帶上參數、該參數可以指定類型等等。RouterSDK 很輕松的實現上面的功能,除此之外,RouterSDK還提供了一些實用的功能如動態路由配置、跳轉動畫、任務站、跳轉前處理等等。

設計

Android系統正常的跳轉方式 A頁面 intent 到 B頁面,B頁面intent C頁面,他們之間的聯系是很親密直接的。彼此間的跳轉都是相互intent,如下圖所示:

但是這樣的通信方式太過直接,導致兩個頁面交集在一起,這時候想在他們之間做些事情是很難的,并且這樣的方式其實不利于解耦。假設每個頁面都是獨立的個體,他們之間的通信通過一個中轉器來處理,然后根據這個中轉器來啟動另外一個頁面。這樣我們就可以通過控制中轉器按照我們的規則跳轉。由于這個中轉器類似于路由器功能,所以把它叫做Router。

如下圖的模型所示:

RouterSDK 架構分為三層:核心層解析、業務層處理、API層調用。這種設計模型是比較經典的稱盒子模型。底層為核心層(不輕易改變)、中間層為業務層(處理業務邏輯)、外層為API層(提供AP調用)。

核心層解析:解析、bundle組裝、匹配路由

業務層處理:路由表操作、處理中斷器、跳轉、動畫等等。

API掉用:三方調用的API

洋蔥結構圖.png

為什么這么設計呢?首先是架構層次分明,通過這三個層次的隔離,從用戶的角度來看是很容易使用的,也對外隱藏了業務邏輯層、核心層的實現細節。而核心層定義的子系統抽象,保證了整個微社區SDK的靈活性、擴展性。

第三方AP啟動模型

應用內需要有個頁面接收第三方的請求,然后通過RouterSDK進行解析uri,轉換成相關的Intent,然后再跳轉到相關的頁面。如圖所示。

A頁面是程序的一個入口,跳轉交給Router處理。這樣我們就可以通過發命令來控制跳轉的頁面了。

三方AP啟動模型.png

實現

首先編寫的是MatchParse類,最里面的一層核心層,就是我上面所說的解析和組裝,這些我們認為是固定的不輕易改變的,這一層原則上調用者不需要知道具體怎么去做。RouterSDK 將解析uri、拆解參數、組裝bundle,最終根據解析的結果以code形式返回上一層。然后在RouterEngine類處理業務相關的,如路由表的增刪改查、interceptor的編寫、跳轉動畫的實現等等。這一層可以根據自己需求增強SDK。這樣維護和擴展起來就非常的清晰了。最后編寫的是API,提供相應的API調用。三方的AP都是通過這層調用SDK的方法和規范入口。

Class Diagram.png

MatchParse 解析 uri,根據rule轉化成不同的類型的參數,最終轉成我們需要的產物bundle。這個規則是約定好的,RouterSDK的規則表如下:

key format {i:ikey} {f:key} {l:key} {d:key} {s:key} {b:key}
type integer float long double string boolean

如:jomeslu://www?{i:id}=168&{s:jomeslu}=jomeslu

  • Scheme:通常定義為 應用某個路徑
  • Host: 通常用于區分不同的頁面,比如activity
  • path : 傳遞參數與參數類型

舉個例子,jomeslu://www?{i:id}=168&{s:jomeslu}=jomeslu ,SDK 會在路由表查找jomeslu://www,找到對應的Class文件,而{i:id}=168&{s:jomeslu}=jomeslu,根據路由規則轉換換成 int id=168 ,String jomeslu = jomslu.然后將這些值換成bundle進行傳值。接bundle值跟系統的寫法是一樣的, int id = getIntent().getIntExtra("id", -1);所以RouterSDK不用關心如何接受值。

private void setKeyValueBundle(String key, String value, Bundle bundle) throws Exception {
        //符合自定義的規則 {s:te}
        if (!TextUtils.isEmpty(key) && key.startsWith(RuleConstant.PARAM_SPIT_LEFT) && key.endsWith(RuleConstant.PARAM_SPIT_RIGHT) &&
                key.contains(RuleConstant.PARAM_SPIT_SIGN)) {
            String realKey = key.substring(key.indexOf(RuleConstant.PARAM_SPIT_SIGN) + 1, key.lastIndexOf(RuleConstant.PARAM_SPIT_RIGHT));
            String type = key.substring(1, key.indexOf(RuleConstant.PARAM_SPIT_SIGN));
            switch (type.toUpperCase()) {
                case RuleConstant.PARAM_B:
                    Boolean aBoolean = Boolean.valueOf(value);
                    bundle.putBoolean(realKey, aBoolean);
                    break;
                case RuleConstant.PARAM_D:
                    bundle.putDouble(realKey, Double.valueOf(value));
                    break;
                case RuleConstant.PARAM_F:
                    bundle.putFloat(realKey, Float.valueOf(value));
                    break;
                case RuleConstant.PARAM_I:
                    bundle.putInt(realKey, Integer.valueOf(value));
                    break;
                case RuleConstant.PARAM_L:
                    bundle.putLong(realKey, Long.valueOf(value));
                    break;
                case RuleConstant.PARAM_S:
                    bundle.putString(realKey, value);
                    break;
            }

        } else if (!TextUtils.isEmpty(key)) {
            //默認是字符串
            bundle.putString(key, value);
        }
    }

RouterEngine 的 getIntent(Context context) 的方法,這個根據MatchParse解析結果 返回相關的intent。根據不同mathcode 我們就知道底層處理了的結果。以后擴展起來就比較方便了。

MatchParse mathchParse = new MatchParse(mRouteTableHandle, context);
     int mathchCode = mathchParse.mathch(param);
     switch (mathchCode) {
                 .....
                 .....
            case RuleConstant.MATHCH_SUCCESS:
                if (param.getmRouterResultCallback() != null) {
                    param.getmRouterResultCallback().succeed(param.getUri());
                }
                if (param.getIRouteInterceptor() != null && param.getIRouteInterceptor().interceptor()) {
                    return null;
                }
                Intent intent = new Intent(context, param.getClazz());
                intent.putExtras(param.getmBundle());
                if (!(context instanceof Activity)) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                return intent;

                 ...

不一一貼代碼了,具體請查看代碼。RouterSDK已經開源。在本文已經寫出下載地址。

總結

本文講解了RouterSDK 的設計與實現,以及編寫SDK的技巧。同時講解了Router框架的優勢以及使用場景。如果有好像的想法請提Issues。

 

 

來自:http://www.jianshu.com/p/c7524b0125df

 

 本文由用戶 MilSoper 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!