从0写一个ARouter

2019-11-26  本文已影响0人  风二叽

        最近项目中越来越多使用组件化,所以记录一下由此产生的最基本的一个问题:路由。

         安卓页面间的跳转想必是每一个学习安卓之人最先接触的,最基本的方式就是直接调用startActivity方法。这种方法在以前整个工程都是单模块的时候没啥大问题,简单也没有必要去重新写新的方法。但是随着目前安卓开发趋向于模块化组件化,如果想用这种方式实现组件间的跳转,势必会造成各个组件相互依赖,造成高耦合度,这就违背了组件化非常重要的一个目的:解耦。这样下来组件就只能适用于当前工程,随着项目页面越来越多,本来作为组件的那部分却移入了越来越多项目中的依赖,这时如果另一个工程想移入这个组件,估计开发人员要炸了,因为每次移入组件,都会有一堆报红,因为这个组件还有很大一部分对原来工程里的逻辑。


        解决思路肯定是解耦,首先想到利用利用scheme进行跳转,统一一个Activity,内部根据Uri获取到domain,再进行统一判断进行跳转,但是这样不够通用,每个activity都得配置一个判断,换一个工程我们又得重新配置一遍,所以不够完美。这样行不通那利用反射机制呢?每个跳转都得配置包名跟类名,这个组件换个工程我们得把所有类名更换一遍,还不如上一个方法。。。。

        我们想要的是不用每个activity都配置一遍跳转的逻辑,只需要根据一个参数就能自动给我们配置好跳转,这样耦合性相当低了。那以上这些都行不通的话,有没有现成的解决方案呢?

        答案是当然有,阿里给我们提供了一个ARouter框架,耦合性低,适合多模块,支持拦截跟定制,路由也只需提供一个注解,无需自定义逻辑,完美解决了上述问题。使用起来也非常简单,就不做记录了,搜一下去github上阿里写了详细的使用说明跟api用法。这里我们需要了解的是,ARouter是如何提供了一套简单的实现,解决了上述问题,本人借鉴了部分源码,将基础逻辑剥离了出来,整理成了个简单的路由框架。

        首先要想了解ARouter原理,你需要先熟悉以下几个知识基础:1.注解跟注解处理器,2.Java apt技术,3.JavaPoet动态依赖注入框架(这个比较重要,其他几个带注解的框架如EventBus,ButterKnife的基本原理也是这个框架)。这些就不做介绍了,不然又是个长篇大论。

我们根据使用ARouter步骤的顺序开始整理思路。

首先跟ARouter一样,先创建三个模块,两个java library跟一个android library:

路由框架模块

分别是注解模块,api模块跟注解处理器模块。

一:注解

先从注解模块开始,我们先定义注册路由地址的注解:

自定义注解

这里设置成对类注解,并设置成编译时注解,比较简单。

一:注解处理器

定义完注解我们需要处理注解处理器。先添加好依赖,引入注解模块,javapoet跟谷歌的auto service主动注册处理器框架:

定义注解处理器RouterProcessor:

在上面添加自动注册的注解,接下来需要实现注解处理器的几个方法:

获取内部工具类,后面会用到 限定该注解处理器处理的注解,这里指定为Router 这个方法最重要,在指定注解被使用后开始执行

这里process方法中核心代码集中在creatJavaCode中,该方法的作用是获取注解信息生成java文件,生成类添加了方法,配置了包名跟实现的接口,我们看下代码:

这里根据拿到的注解外部类的信息编译时自动生成代码,代码的作用是将注解的外部类的路由信息跟路径保存在一个map中。

我们看看生成的代码,跟我们构建代码部分设置的一样,存储的类实现了IRouterZ接口(到后面就知道为什么要专门实现一个接口而不只是添加方法了):

这样一来最核心的路由信息存储部分就完成了,它为我们省去了每个activity配置跳转信息的操作,不得不感慨javapoet这个功能是真的强大。。。

三.读取

接下来跳转页面我们就根据存下来的路由map进行跳转,那么问题来了,编译完之后我们如何从动态生成的代码中获取到路由信息呢?

ARouter的思路是在初始化的时候就从生成的所有dex中去寻找实现了IRouterZ的类,这部分类即是动态构建代码时生成的,然后传一个相同的map进去依次执行所有的onLoad方法,这样所有的路由信息就全部都保存到了我们自己创建的map中去,思路有了我们看看具体代码:

既然是在初始化时候实现那肯定是Init方法作为入口:

init方法没啥信息,我们看LogisticsCenter.init(context)里面执行了什么:

再顺势跳到registerComm里面:

可以看出,这个方法从生成代码时候配置的包名里面遍历所有的class,找到实现了IRouterZ的类,最后执行onLoad方法,将路由信息存入Warehouse.routeMap中去,Warehouse.routeMap是个存放path跟路由信息的map:

三.路由跳转

好了,最关键的配置,存储跟读取都实现了,最后一步的跳转其实相对来说就很简单了,因为在存储的时候我们获取到注解的外部类,比如activity,fragment,获取到了这些类其实最后就用最基本的startActivity就行了,当然,路由信息的结构在写框架时你可以自己定义,所以跳转前判断逻辑也可能不尽相同,这里我们还是参照ARouter写,以activity为例:

首先activity中调用:

我们依次看这几个方法,先看build:

可以看出build方法实际上是根据地址new了一个PostCard对象,PostCard继承了路由信息所在的类,因此在这里它当做路由信息来用。

bundle方法实际上就是个set方法,将Bundle数据传给postcard对象。

最后关键的navigation方法肯定是跳转了,我们看下怎么实现的:

跳到ZRouter的navigation方法中去:

因为最开始Postcard只有path被赋值了,单单靠这个无法完成跳转,那么一直低调不被注意的Warehouse.routeMap终于派上用场了,这个我们把它称之为路由表,在LogisticsCenter.complete(postcard);方法中,我们根据路由表完善了Postcard的各个参数,我们看看:

setDestination设置了目标类,setRouteType设置了跳转的类型,这里是activity,因此根据上面代码执行的是startActivity(postcard)方法,再看这个方法:

这部分就很简单了,设置启动模式,设置intent传参,最后在主线程运行startActivity完成跳转。

由此,ARouter的基本功能也就实现了,看到最后可以总结出,归根结底还是运用的startActivity,只不过判断信息都巧妙的存放在了路由表里而已。

上一篇下一篇

猜你喜欢

热点阅读