路由是现如今移动端开发中必不可少的功能,尤其是企业级APP,可以用于将Intent页面跳转的强依赖关系解耦,同时减少跨团队开发的互相依赖问题。
对于大型APP开发,基本都会选用模块化(或组件化)方式开发,对于模块间解耦要求更高。TheRouter是一整套完全面向模块化开发的解决方案,不仅能支持常规的模块依赖解耦、页面跳转,同时提供了模块化过程中常见问题的解决办法。例如:完美解决了模块化开发后由于组件内无法获取Application生命周期与业务流程,造成每次初始化与关联依赖调用都需要跨模块修改代码的问题。
Navigator:
ServiceProvider:
FlowTaskExecutor:
ActionManager:
注:FlowTaskExecutor、ActionManager后续会作为可选能力,提供可插拔或单独使用的选项(预计10月份提供)。
目前现有的路由基本上集中于两种能力的实现:页面跳转、跨模块调用,核心技术方案大体上如图:
TheRouter的页面跳转、跨模块调用也是如此,但是在设计上会有一些细节处理。
TheRouter会在编译期根据注解生成RouteMap__开头的类,这些类中记录了当前模块的所有路由信息,也就是当前模块的路由表。
在最顶层的app模块中,通过Gradle插件,将所有aar、源码中的RouteMap__开头的类统一集中到TheRouterServiceProvideInjecter类中。
后续应用启动后,初始化路由时只需要执行TheRouterServiceProvideInjecter类的方法,就能没有任何反射的加载到全部的路由表了。
加载以后的路由表会被保存到一个支持正则匹配的Map中,这也是TheRouter允许多个path对应同一个落地页的原因。每当发生页面跳转时,通过跳转时的path,去Map中获取到对应的落地页信息,再正常调用startActivity()即可。
参数释义
description="第二个页面",params={"hello","world"})publicclassHomeActivityextendsAppCompatActivity{
}
3.2发起页面跳转传入的参数可以是String和8种基本数据类型、也可以是Bundle、Serializable、
Parcelable对象,跟Intent传值规则一致。
同时也支持为本次跳转的Intent添加Flag/Uri/ClipData/identifier等业务特殊参数。
路由表生成规则:编译期按照如下顺序取并集。
覆盖规则:
根据如下顺序,如果相同,后者可以覆盖前者的路由表规则。
TheRouter的路由表是动态添加的,项目每次编译后,会在apk内生成一份当前APP的全量路由表,默认路径为:/assets/therouter/routeMap.json。这个路由表也可以后续通过远程下发的方式使用,例如远端可以针对不同的APP版本,下发不同的路由表达到配置目的。这样如果将来线上某些页面发生Crash,可以通过将这个页面的落地页替换为H5的方式,临时解决这类问题。
有两种推荐的远程下发方式可供使用方选择:
注:一旦你设置了自定义的InitTask,原框架内路由表初始化任务将不再执行,你需要自己处理找不到路由表时的兜底逻辑,一种建议的处理方式见如下代码。
ServiceProvider的核心设计思想也是这样的,目前服务间的调用协议采用接口的方式。当然,也可以兼容不通过接口下沉而是直接调用的情况。
具体到Android侧就是AIDL类似的设计,只是要比AIDL开发简单很多:
例如上面的图片:拉拉需要使用录音的服务,小货则向外提供一个录音的服务,由TheRouter的ServiceProvider负责撮合。
她无需关心,IRecordService这个接口服务是谁提供的,他只需要知道自己需要使用这样的一个服务就行了。
注:如果没有提供服务的提供方,TheRouter.get()可能返回null
每个希望被自动初始化的方法,必须使用publicstatic修饰,主要原因是这样子就能通过类名直接调用了。另外很多初始化代码都需要获取Context对象,所以我们将Context作为初始化方法的默认参数,会自动传入Application。其他的所在类名、方法名都没有限制,反正只要加上了@FlowTask注解,在编译期都能通过APT获取到。
@FlowTask注解参数说明:
/***将会在异步执行*/@FlowTask(taskName="mmkv_init",dependsOn=TheRouterFlowTask.APP_ONCREATE,async=true)publicstaticvoidtest2(Contextcontext){System.out.println("异步=========ApplicationonCreate后执行");}@FlowTask(taskName="app1")publicstaticvoidtest3(Contextcontext){System.out.println("main线程=========应用启动就会执行");}/***将会在主线程初始化*/@FlowTask(taskName="test",dependsOn="mmkv,app1")publicstaticvoidtest3(Contextcontext){System.out.println("main线程=========在app1和mmkv两个任务都执行以后才会被执行");}5.2内置初始化节点使用这个能力,在路由内部默认支持了两个生命周期类任务,可在使用时直接引用
同时,使用TheRouter的自动初始化依赖,也无需担心循环依赖造成的问题,框架会在编译期构建有向无环图,监测循环依赖情况,如果发现会在编译期直接报错,并且还会将发生循环引用的任务显示出来,用于排错。
当所有aar都编译完成,生成好全部的Task以后,会在主app中通过Gradle插件进行聚合,在这时会将所有的Task做一次检查,通过构建有向无环图来防止Task发生循环引用的情况。
每次应用启动后,会在路由初始化时,将有向图中的全部Task,按照依赖关系按顺序加载。
Action本质是一个全局的系统回调,主要用于预埋的一系列操作,例如:弹窗、上传日志、清理缓存。
//action建议遵循一定的格式constvalACTION="therouter://action/xxx"@FlowTask(taskName="action_demo")funinit(context:Context)=TheRouter.addActionInterceptor(ACTION,object:ActionInterceptor(){overridefunhandle(context:Context,args:Bundle):Boolean{//dosomethingreturnfalse}})执行一个Action:
abstractclassActionInterceptor{abstractfunhandle(context:Context,args:Bundle):BooleanfunonFinish(){}/***数字越大,优先级越高*/openvalpriority:Intget()=5}6.3客户端动态响应使用场景如果仅客户端使用,常用的场景可能是:当用户执行某些操作(打开某个页面、H5点击某个按钮、动态页面配置的点击事件)时,将会自动触发,执行预埋的Action逻辑。
TheRouter提供了图形化界面的迁移工具,可以一键从其他路由迁移到TheRouter,目前仅支持ARouter,其他路由框架迁移也在开发中(GitHub下载,70M左右,请耐心等待):
如果项目中使用了ARouter的IProvider.init()方法,可能需要手动处理初始化逻辑。
如下图:
功能
TheRouter
ARouter
WMRouter
Fragment路由
支持依赖注入
加载路由表
无运行时扫描无反射
运行时扫描dex反射实例类性能损耗大
运行时读文件反射实例类性能损耗中
注解正则表达式
Activity指定拦截器
(四大拦截器可根据业务定制)
导出路由文档
(路由文档支持添加注释描述)
动态注册路由信息
APT支持增量编译
(开启文档生成则无法增量编译)
plugin支持增量编译
多Path对应同一页面(低成本实现双端path统一)
远端路由表下发
支持单模块独立初始化
支持使用路由打开第三方库页面
对热修复支持(例如tinker)
(未改变的代码多次构建无变动)
(多次构建apt产物会发生变化,生成无意义补丁)
TheRouter并不仅仅是一个小巧灵活的路由库,而是一整套完整的Android模块化解决方案,能够解决几乎全部的模块化过程中会遇到的问题。
对于现有的路由框架,我们也在最大限度支持平滑迁移,目前已完成ARouter的一键迁移工具,其他框架的迁移仍在开发中。你也可以在Githubissue中提出需求,我们评估后会尽快支持,也欢迎任何人提供PullRequests。