基于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…

相关文章
相关标签/搜索