ARouter解析一:基本使用及页面注册源码解析

ARouter是阿里2017年开源的页面路由框架,是今年比较火的一个开源框架,目前在Github上已经有2.2k的小星星了。官方对这个框架的定义是ARouter是阿里巴巴开源的Android平台中对页面、服务提供路由功能的中间件,提倡的是简单且够用。为了跟上潮流,我也打算来学习下这个开源框架,整个预计会分成四五个系列。今天我们先来看下基本使用和页面注册的源码。

Google提供的原声路由主要是通过intent,可以分成显示和隐式两种。显示的方案会导致类之间的直接依赖问题,耦合严重;隐式intent需要的配置清单中统一声明,首先有个暴露的问题,另外在多模块开发中协作也比较困难。只要调用startActivity后面的环节我们就无法控制了,在出现错误时无能为力,而ARouter可以在跳转过程中进行拦截,出现错误时可以实现降级策略,这个我们今天就不涉及到,后面我们会专门讲。今天的分享会从下面几个方面。

  • ARouter配置
  • activity页面之间跳转
  • APT技术
  • SPI技术
  • 页面注册源码解析

我们就不直接撸demo了,直接使用官方的demo进行分享。


demo.png

1.ARouter配置

配置之前我们先看下官方宣称的ARouter的优势:

  • 直接解析URL路由,解析参数并赋值
  • 支持多模块项目
  • 支持InstantRun
  • 允许自定义拦截器
  • ARouter可以提供IoC容器
  • 映射关系自动注册
  • 灵活的降级策略
    简单讲,就是很牛X,几乎包含页面跳转的所需要的所有功能。

在使用之前需要先配置gradle,目前api最新的版本是1.2.1.1,处理器的最新版本是1.1.2.1.

android {
    defaultConfig {
    ...
    javaCompileOptions {
        annotationProcessorOptions {
        arguments = [ moduleName : project.getName() ]
        }
    }
    }
}

dependencies {
    // 替换成最新版本, 需要注意的是api
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    compile 'com.alibaba:arouter-api:1.2.1.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.2.1'
    ...
}

接着需要尽早的初始化ARouter,可以考虑在Application中进行初始化:

ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化

初始化工作就是这样,接着我们看下页面跳转怎么玩。

2.页面跳转使用

首先需要在支持路由的页面上添加注解,路径path至少需要有两级,比如我们需要跳转到Test2Activity,那么需要在activity上面配置path,具体内容不一定是/test/activity2,可以自由发挥。

@Route(path = "/test/activity2")
public class Test2Activity extends AppCompatActivity

然后直接像下面这样就行了,build中的参数就是上面配置的路径。

ARouter.getInstance().build("/test/activity2").navigation();

很简单有木有?


activity跳转.png

如果需要跳转的时候携带参数呢?

ARouter.getInstance().build("/test/activity2").withString("key1", "value1")
.navigation();

是不是so easy?ARouter提供了很多的withXX方法,可以携带基本类型,Object,Parcelable等等,里面的原理也是通过Bundle进行携带mBundle.putXX

activity跳转带参数.png

如果希望实现类似startActivityForResult呢?只需要在navigation中传入两个参数,第一个就是Context,第二个参数是requestCode。

ARouter.getInstance().build("/test/activity2")
                     .withString("key1", "value1")
                     .navigation(this, 999);

在Test2Activity中就可以传递结果:

setResult(int resultCode)
或者
setResult(int resultCode, Intent data)

这里为了偷懒我们只是setResult(100);
然后我们就可以在onActivityResult中得到数据,根据requestCode可以拿到数据,我们这里只是弹个Toast。

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 999:
                Toast.makeText(this, "get  resultCode:" + String.valueOf(resultCode), Toast.LENGTH_LONG).show();
            default:
                break;
        }
}
activtyForResult.png

基本跳转就是上面几种情况,接下来我们来看看源码是怎么实现的。

3.APT技术

APT,就是Annotation Processing Tool 的简称,就是可以在代码编译期间对注解进行处理,并且生成Java文件,减少手动的代码输入。这里我们就不多做介绍,这个不太清楚的可以参考我之前的分享Android模块开发之APT技术

4.SPI技术

Java提供的SPI全名就是Service Provider Interface,其实就是为某个接口寻找服务的机制,有点类似IOC的思想,将装配的控制权移交给ServiceLoader。SPI在平时我们用到的会比较少,但是在Android模块开发中就会比较有用,不同的模块可以基于接口编程,每个模块有不同的实现service provider,然后通过SPI机制自动注册到一个配置文件中,就可以实现在程序运行时扫描加载同一接口的不同service provider。不太清楚的可以参考之前的分析Android模块开发之SPI

5.页面注册源码分析

分析源码之前我们需要先了解下框架的整个架构,这里直接引用官方的一张图片。

  • 最基础的就是Compiler这个SDK,主要是用来在编译期间处理注解Router/Interceptor/Autowire三个注解,在编译期间自动注册注解标注的类,成员变量等。
  • API的SDK是用户在运行期使用。Launcher这一层只有ARouter和_ ARouter, ARouter就是我们需要打交道的接口,这里使用了代理模式,实际ARouter是通过_ ARouter进行工作。
  • 第二层也是绿色的,用户也是可以调用到,这里主要是提供接口,我们可以扩展服务,比如拦截器服务,成员变量装配服务等,Template主要是提供模版接口,这个SDK会在编译期生成一些映射文件,而这些映射文件会按照Template组件中提供的模板来生成,这样按照一定的规则和约束生成映射文件也方便Route在运行的时候进行读取。
  • Ware House类似一个仓库,主要存储ARouter在运行期间加载的一些配置文件以及映射关系。
  • Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的。
  • 再下一层就是Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层。
ARouter架构.png

我们先看下三个主要的注解,这个我删掉了很多,有需要的可以自行参考源码。可以看出来三个注解的有效期间都是编译期间,关于注解不太清楚的可以参考我们之前的分享反射注解与动态代理综合使用

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {

    /**
     * Path of route
     */
    String path();
    ……
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
    /**
     * The priority of interceptor, ARouter will be excute them follow the priority.
     */
    int priority();

    /**
     * The name of interceptor, may be used to generate javadoc.
     */
    String name() default "Default";
}

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {

    // Mark param's name or service name.
    String name() default "";

    // If required, app will be crash when value is null.
    // Primitive type wont be check!
    boolean required() default false;

    // Description of the field
    String desc() default "No desc.";
}

其实小伙伴们也可以猜出来,ARouter会在编译的时候将Router注解标注的activity扫描出来,并且按照定义好的模版生成装配关系的代码。这个其实就是APT技术,我先前已经做好铺垫,英明神武吧:)。简单说就是要自定义注解的处理器,继承自AbstractProcessor,需要自己实现process方法来处理注解,然后需要用到SPI技术来注册我们自己定义的注解处理器,之后JVM就能在编译期间通过serviceLoader来找到我们的注解处理器balabala。这里篇幅限制我们就不展开反复说了,不清楚的同学自行参考上面的链接。
ARouter的process方法比较长,我们就看下生成的文件放在哪里,包名就是public static final String PACKAGE_OF_GENERATE_FILE = "com.alibaba.android.arouter.routes";
那么文件名呢?还记得我们前面跳转Test2Activity的path是什么吗?@Route(path = "/test/activity2")其中test就是group,所以我们文件名就是groupFileName = ARouter$$Group$$test

public static final String SEPARATOR = "$$";
public static final String PROJECT = "ARouter";
public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + "Group" + SEPARATOR;
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
String groupFileName = NAME_OF_GROUP + groupName;
                JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                        TypeSpec.classBuilder(groupFileName)
                                .addJavadoc(WARNING_TIPS)
                                .addSuperinterface(ClassName.get(type_IRouteGroup))
                                .addModifiers(PUBLIC)
                                .addMethod(loadIntoMethodOfGroupBuilder.build())
                                .build()
                ).build().writeTo(mFiler);
}
activity装配关系.png

那么我们来看看ARouter自动生成的activity装配关系是怎么样的。

public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
  }
}

有一个需要注意的地方就是put("key1", 8),还记得我们上面参数跳转应该是withString("key1", "value1"), 为什么这里是8?其实在编译的时候做了处理,8代表类型String。

public enum TypeKind {
    // Base type
    BOOLEAN,
    BYTE,
    SHORT,
    INT,
    LONG,
    CHAR,
    FLOAT,
    DOUBLE,

    // Other type
    STRING,
    PARCELABLE,
    OBJECT;
}

页面自动注册的源码基本就是上面这些内容,我们最后引用一张官方的图片。

首先通过注解处理器扫出被标注的类文件;然后按照不同种类的源文件进行分类,不仅仅提供了跳转功能,它也能够实现模块之间的解耦,其实ARouter中的所有组件都是自动注册的

在按照不同种类的源文件进行分类完成之后,就能够按照固定的命名格式生成映射文件,这部分完成之后就意味着编译期的部分已经结束了;

而最后一步的初始化其实是发生在运行期的,在运行期只需要通过固定的包名来加载映射文件就可以了,这就是页面自动注册的整个流程。

注解&注解处理器.png

6.总结

到这里相信小伙伴们对于ARouter页面跳转的基本使用已经很熟悉了。我们也简单分析了下页面注册的原理,ARouter可以通过注解自动注册并且在编译期间生成映射关系,在运行的时候就可以加载文件,通过path就可以顺利跳转到目标页面。

好了,今天车就开到这了。估计再说下去小伙伴们该睡觉了,关于加载配置文件和页面跳转我们就在解析二中分析,欢迎关注。

你们的赞是我最大的动力,谢谢!

关于注解和APT技术与SPI技术不太清楚的小伙伴可自行参考下面链接:
Android模块开发之APT技术
Android模块开发之SPI

欢迎关注公众号:JueCode

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容