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

本文會詳細介紹了RouterSDK框架的設計與實踐,經過這篇文章不但能夠知道Router框架的一些功能,並且還能夠提供實現SDK的一些思路。RouterSDK已經開源,下載地址:github.com/Jomes/route…javascript

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

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

tmpdir--17_2_9_19_40_40.png

可是這樣的通訊方式太過直接,致使兩個頁面交集在一塊兒,這時候想在他們之間作些事情是很難的,而且這樣的方式其實不利於解耦。假設每一個頁面都是獨立的個體,他們之間的通訊經過一箇中轉器來處理,而後根據這個中轉器來啓動另一個頁面。這樣咱們就能夠經過控制中轉器按照咱們的規則跳轉。因爲這個中轉器相似於路由器功能,因此把它叫作Router。
以下圖的模型所示:
android

tmpdir--17_2_9_19_47_07.png

RouterSDK 架構分爲三層:核心層解析、業務層處理、API層調用。這種設計模型是比較經典的稱盒子模型。底層爲核心層(不輕易改變)、中間層爲業務層(處理業務邏輯)、外層爲API層(提供AP調用)。
核心層解析:解析、bundle組裝、匹配路由
業務層處理:路由表操做、處理中斷器、跳轉、動畫等等。
API掉用:三方調用的APIgit

洋蔥結構圖.png

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

第三方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。
RouterSDk下載地址:github.com/Jomes/route…

###推薦
推薦一個學習源碼的站點:androidblog.cn/index.php/S…

相關文章
相關標籤/搜索