转载请注明出处:王亟亟的大牛之路

有一段时间没更新博客了,最近也没学什么新东西,正好组里小伙在做路由跳转的一个“公共库“,然后正好最近这样的轮子不少,我也就跟着看看,学习一下人家的思路,然后推荐给大家这个库

https://github.com/alibaba/ARouter

分析讲解之前先安利:https://github.com/ddwhan0123/Useful-Open-Source-Android (总有些你用得到的东西,欢迎补issues)


什么是路由跳转?为什么要用路由跳转?

路由跳转

web开发框架一般支持用户设置路由表,让表内的页面/层级,产生可互相跳转,转发等行为(如果理解不正确请指出)

要用的理由1

项目大了就无法获取到其他包的Activity.class了

要用的理由2

逻辑清晰,比较语义化,清楚的知道跳转路径和目的地

要用的理由3

不单单可以应用于普通Activity还可以与浏览器做一些业务逻辑。(如果有遗漏请指出)


ARouter所实现的功能:

支持直接解析URL进行跳转、参数按类型解析,支持Java基本类型(*)

支持应用内的标准页面跳转,API接近Android原生接口

支持多模块工程中使用,允许分别打包,包结构符合Android包规范即可(*)

支持跳转过程中插入自定义拦截逻辑,自定义拦截顺序(*)

支持服务托管,通过ByName,ByType两种方式获取服务实例,方便面向接口开发与跨模块调用解耦(*)

映射关系按组分类、多级管理,按需初始化,减少内存占用提高查询效率(*)

支持用户指定全局降级策略

支持获取单次跳转结果

丰富的API和可定制性

被ARouter管理的页面、拦截器、服务均无需主动注册到ARouter,被动发现 支持Android N推出的Jack编译链

内容来自:https://github.com/alibaba/ARouter/blob/master/README_CN.md


案例分析

官方提供了一个demo.apk,下载下来跑了跑,长这样

我们跟着demo去看他内部的实现,顺道看下how to use?

初始化:

在你要使用这个库之前,必须初始化,也就是调用

protected static synchronized boolean init(Application application) 

其实这方法长这样,他也没叫你穿Context了,很直白直接叫你传Application,那我们就要在自定义Application里的onCreate里把他给初始化了,不初始化的话会丢一个

throw new InitException("ARouterCore::Init::Invoke init(context) first!");

日志:

内部维护了一个简单的log类:ILogger,也就是一些简单的打日志的那些东西 .d .e .i 这些,主要是为了给予这个类一个状态“是否打印日志“

而是否打印日志,调用

 static synchronized void openLog() 

在内部调用了

logger.showLog(true);

然后就有log了,无论是跳转过程还是拼接过程都可以看到具体内容,便与调试

调试:

因为log已经可以很好的帮你监控跳转行为的各个流程,官方缩写的调试方法更简便,当然首先你得开启

 static synchronized void openDebug()

开完之后有什么区别呢?

当你调用navigation()方法后,他会弹一个Toast,告诉用户一些路由地址和分组的值。

路由地址很好理解,那组的概念又是什么呢?


跳转:

Activity之间的跳转是最为常见的了,ARouter对其进行非常有效的封装,像这样

ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .navigation();

我们来看一下他是如何实现的

首先先获取ARouter的实例,内部没有什么复杂操作,首先判断有没有初始化,如果初始化了再盼空,如果为空就创建一个ARouter对象,然后将其返回。

获取实例之后,先构建路径build

 protected Postcard build(String path) {
        if (StringUtils.isEmpty(path)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return build(path, extractGroup(path));
        }
    }

首先先判空,如果路径没东西就抛异常

不为空之后把时间逻辑交由PathReplaceService接口来处理

PathReplaceService 接口用于处理path相关逻辑,如果要自定义path处理方法可自行二次实现

分发完后调用了另外个build方法

  protected Postcard build(String path, String group) {
        if (StringUtils.isEmpty(path) || StringUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return new Postcard(path, group);
        }
    }

行为几乎一致,但是这里把我们的路径分配到了默认组内并生成新的Postcard对象返回

看到这里有点尴尬,Postcard是什么鬼?

Postcard在com.alibaba.android.arouter.facade目录下是一个包含路线图的容器

里面有一些我们一看就懂的字段

    private Uri uri;
    private Object tag;
    private Bundle mBundle;
    private int flags = -1;
    private int timeout = 300;
    private IProvider provider;
    private boolean greenChannal;

很明显他就是整个路由行为的一个载体,可分配url,group,path等等,既然是一个是载体,那我们就不管他干啥,反正就是一个带信息传递用的“快递小哥”

无论怎么创建其实都是走的下面这个构造函数,字面就很好理解,设置的path和group,Bundle有就有没有就创建,uri暂时是空

 public Postcard(String path, String group, Uri uri, Bundle bundle) {
        setPath(path);
        setGroup(group);
        setUri(uri);
        this.mBundle = (null == bundle ? new Bundle() : bundle);
    }

例子里我们是传参了,也就是对bundle进行了一系列的native的put操作。

navigation方法经过一系列的方法又回到了

ARouter.getInstance().navigation(context, this, -1, callback);
 protected Object navigation(final Context context, final Postcard postcard, final int requestCode, NavigationCallback callback)

在调用之初

requestCode 是 -1

postcard 就是前面创建的Postcard对象

callback是个null

Context 是Application的context对象

 protected Object navigation(final Context context, final Postcard postcard, final int requestCode, NavigationCallback callback) {
        try {
            //注册监听,去拆对象里的属性,做包装
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            //刚才提到的debuggable为true时候打出一些内容
            if (debuggable()) { // Show friendly tips for user.
                Toast.makeText(mContext, "There's no route matched!\n" +
                        " Path = [" + postcard.getPath() + "]\n" +
                        " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show(
            if (null != callback) {
                callback.onLost(postcard);
            } else {    // No callback for this invoke, then we use the global degrade service.
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                    //因为ARouter实例不为空所以接口不为空所以把Lost⌚️分发了出去
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

            return null;
        }
        //如果回调不为空就把监听到的回调进行分发
        if (null != callback) {
            callback.onFound(postcard);
        }
        //绿色异步通道的处理
        if (!postcard.isGreenChannal()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            LogisticsCenter.interceptions(postcard, new InterceptorCallback() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @Override
                public void onInterrupt(Throwable exception) {
                    logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
        } else {
            return _navigation(context, postcard, requestCode);
        }

        return null;
    }

上面有一些新名词,诸如绿色通道等,这部分可以去翻官方的readme,里面有介绍

这里看到,最后又调用了

 private Object _navigation(final Context context, final Postcard postcard, final int requestCode)

这是具体跳转行为的一个方法,他支持 多种类型的postcard type,我们主要看下ACTIVITY相关的

 case ACTIVITY:

                // 构建 intent
                Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                // 设置 flags.
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else {
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // Judgment activity start type.
                if (requestCode > 0) {  // RequestCode exist, tell us user's want startActivityForResult, so this context must son of activity.
                    ((Activity) currentContext).startActivityForResult(intent, requestCode);
                } else {
                    currentContext.startActivity(intent);
                }

                break;

最终实现就是我们常用的 startActivity和startActivityForResult操作


总结:

其实它做的就是普通的跳转的行为,但是对跳转进行了很丰富的二次实现。

1.降低了协同开发的成本,因为你根本不在意跳转目的地是否真的存在,按照预先定下来的url跳转就好(包括传参数)。

2.无论外部内部跳转变得更简单,反正初始化就会构建一个路由管理器,无需自己管理跳转过程。

3.测试方便,跟web测转发一样,单元测试实现性更强。

本来还想介绍下ARouter的浏览器拦截和自定义url拼接,但是篇幅问题,这篇就不写了,以后有机会再写吧

类似实现的开源库:

https://github.com/mzule/ActivityRouter

https://github.com/joyrun/ActivityRouter

翻翻git之---丰富多样的路由跳转开源库 ARouter的更多相关文章

  1. vue路由跳转时判断用户是否登录功能

    通过判断该用户是否登录过,如果没有登录则跳转到login登录路由,如果登录则正常跳转. 一丶首先在用户登录前后分别给出一个状态来标识此用户是否登录(建议用vuex): 简单用vuex表示一下,不会可以 ...

  2. Extjs6(四)——侧边栏导航根据路由跳转页面

    本文基于ext-6.0.0 之前做的时候这个侧边栏导航是通过tab切换来切换页面的,但是总感觉不太对劲,现在终于发现怎么通过路由跳转了,分享给大家,可能有些不完善的地方,望大家读后可以给些指点.欢迎留 ...

  3. 关于elementUi tab组件路由跳转卡死问题

    好久没来了,周五项目终于要上线了(*^▽^*),上线之前测出一个很恶心的bug真真是... 项目:Vue + elementUi   后台管理项目 问题描述:登录后首次通过侧边栏路由跳转到主页面有ta ...

  4. 10. vue axios 请求未完成时路由跳转报错问题

    axios 请求未完成时路由跳转报错问题 前两天项目基本功能算是完成了,在公司测试时遇到了遇到了一个问题,那就是在请求未完成时进行路由跳转时会报错,想了几种办法来解决,例如加loading,请求拦截, ...

  5. react-router(v4) 路由跳转后返回页面顶部问题

    遇到的问题 由A页面跳转到B页面,B页面停留在A页面的位置,没有返回到顶部. 问题分析 首先分析下出现此问题的原因: 在项目中使用的是 hashHistory,它是建立在 history 之上的,当路 ...

  6. 2种方式解决vue路由跳转未匹配相应路由避免出现空白页面或者指定404页面

    https://www.cnblogs.com/goloving/p/9254084.html https://www.cnblogs.com/goloving/p/9254084.html 1.路由 ...

  7. vue设置路由跳转参数,以及接收参数

    最近做Vue项目,遇到了一个路由跳转问题:首页要跳转到项目页指定的Tab选项卡项,一开始总是跳到默认项.解决方法如下: 在跳转链接处设置了路由跳转参数,如下: <router-link  :to ...

  8. Taro-ui TabBar组件使用路由跳转

    1. 安装taro-ui (此处使用cnpm) cnpm install taro-ui 2. 全局引入样式 app.scss sass :@import "~taro-ui/dist/st ...

  9. App设计:消息推送和界面路由跳转

    概要 app消息推送.显示通知栏,点击跳转页面是很一般的功能了,下面以个推为例演示push集成,消息处理模块及app内部路由模块的简单设计. 推送 推送sdk集成 集成sdk步骤根据文档一步步做就行了 ...

随机推荐

  1. Python实现HMM(隐马尔可夫模型)

    1. 前言 隐马尔科夫HMM模型是一类重要的机器学习方法,其主要用于序列数据的分析,广泛应用于语音识别.文本翻译.序列预测.中文分词等多个领域.虽然近年来,由于RNN等深度学习方法的发展,HMM模型逐 ...

  2. 并发编程 - 线程 - 1.开启线程的两种方式/2.进程与线程的区别/3.Thread对象的其他属性或方法/4.守护线程

    1.开启线程的两种方式: 进程,线程: 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合)而线程才是cpu上的执行单位) 1.同一个进程内的多个线程共享该进程内的地址资源 2.创建线 ...

  3. FreeMarker 的使用方法

    1.FreeMarker 概述 FreeMarker 是一个用Java语言编写的模板引擎,使用模板来生成文本输出;主要用于做静态页面或页面展示; 2.FreeMarker 使用 // 导入jar包: ...

  4. Python作用域-->闭包函数-->装饰器

    1.作用域: 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我要理解两点:a.在全局不能访问到局部 ...

  5. python 面向对象(进阶篇)转载武沛齐

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  6. 分类,logistic回归

    1. 使用回归进行分类 机器学习中分类是指输入一个样本点,输出这个样本点所属的类别,预测的是一个离散值,如类别(1,2). 而回归问题是输入一个样本点,预测一个值,这个值是连续值,可以介于\([1,2 ...

  7. Purpose of ContextLoaderListener in Spring

    The ApplicationContext is where your Spring beans live. The purpose of the ContextLoaderListener is ...

  8. 如何查看windows某个目录下所有文件/文件夹的大小?

    如何查看windows某个目录下所有文件/文件夹的大小? TreeSize Free绿色汉化版是一款硬盘空间管理工具,用树形描述出来,能够显示文件大小和实际占用空间数及浪费的空间等信息,让你做出相应的 ...

  9. jquery插件开发快速入门

    1.添加jQuery对象方法添加jQuery对象方法:jQuery.prototype.myMethod. 在jQuery源码中有一句:jQuery.fn = jQuery.prototype,也就是 ...

  10. Android Studio下编译调试 ndk 的示例

    https://github.com/googlesamples/android-ndk https://github.com/googlesamples android studio 手动安装cma ...