滴滴开源DRouter:一款高效的Android路由框架-程序员宅基地

技术标签: spring  java  android  移动开发  分布式  

桔妹导读:DRouter是滴滴乘客端自研的一套Android路由框架,基于平台化解耦的思想,为组件间通信服务。该项目以功能全面、易用为原则,支持各种路由场景,在页面路由、服务获取和过滤、跨进程及跨应用、VirtualApk插件支持等方面都能提供多样化的服务。目前已在滴滴乘客端、顺风车、单车、国际化、滴滴定制车等十多个滴滴的app内使用,得到各种场景的验证。

1. 

DRouter简介

随着业务不断的发展,业务变的种类繁多,项目代码集中且耦合在一起,导致编译速度慢、开发效率低、维护困难、牵一发动全身。这时无论是从工程效能还是业务迭代稳定性的角度考虑,都需要把项目代码拆分成独立的组件。当组件拆分后,在没有代码耦合的前提下,如何快速、方便、灵活的进行组件间跳转、数据通信、生命周期控制是DRouter要解决的问题。

DRouter支持的功能

  • 使用URI字符串导航Activity、Fragment、View、RouterHandler,支持正则表达式

  • 回调式onActivityResult

  • RouterHandler、Activity支持等待异步完成(hold),并可设置超时时间

  • RouterHandler指定执行线程

  • 注入拦截器,支持全局拦截器和局部拦截器,面向切面编程

  • 更为多样化的Fragment页面跳转能力

  • 使用接口或基类导航到实现类Service的Class以及实例

  • 支持Service别名以及多维过滤器查找

  • 导航Service可指定任意构造器、支持单例

  • 支持动态注册RouterHandler、Service,绑定生命周期自动解绑

  • 简单易用的跨进程执行RouterHandler、Service

  • 跨进程访问无需提前绑定、如同本地调用一样进行访问

  • 支持客户端进程和服务端进程自动重连

  • 支持VirtualApk

2. 

为什么要开发DRouter  

滴滴乘客端包容万象,拥有众多的业务和组件,早在15年就已经启动组件化重构。使用滴滴自研的基于JavaSPI插件技术,动态的生成从接口到实现的映射关系,完成了组件化的通信功能,并且应用到了整个滴滴的Android体系中。虽然SPI性能优良、使用便捷,但同时功能单一、支持的场景少也是一直存在的问题。随着组件化实践的常态化,急需要一款功能强大,适合滴滴的场景,定制化程度高的路由框架。

 

目前市面上已经存在几款路由框架了,比如ARouter,WMRouter。不过滴滴业务繁多,平台化遍布各种场景,对路由的定制化需求强烈,很多功能市面上的路由是不支持的,比如:

 

  • ARouter作为路由的先行者,由于开发较早,功能简单、同时路由查找过程性能损耗较大

  • WMRouter在路由的性能上仍然有一定优化空间,同时没有导航到Fragment/View、回调式ActivityResult,使用ServiceLoader稍显繁琐且无法动态注册以及多维过滤

  • 现今没有一款路由框架对完整的跨进程和跨应用有较好的支持,这在滴滴定制车团队有着强诉求

  • 同时没有一款路由对Fragment提供更多的扩展能力,DRouter在鸿鹄和车载屏项目以及更多Fragment的场景可以提供支持

 

基于以上问题,从18年开始自研了DRouer路由框架,为滴滴的平台化服务几十种场景。

3. 

DRouter有哪些亮点

插件增量编译、多线程扫描,运行时异步加载路由表

路由表在编译期通过插件动态生成。插件会启动多线程同时异步处理所有的组件;增量扫描功能可以帮助开发者在第二次编译时,只对修改过的代码进行处理,极大地缩短路由表生成的时间。

 

在本人的开发机上测试,19年滴滴乘客端扫描5.5万个类,全量需要不到6s的时间;如果是修改了application模块,增量扫描只需要处理修改的单类,耗时0.4s时间;如果是修改了library组件,增量扫描需要扫描整个jar包,根据jar包的大小时间会稍多一些,例如滴滴专快组件增量耗时3.9s。

 

另外框架初始化的时候启动子线程去加载路由表,不阻塞主线程的执行,尽其所能提高效率。

2 完整的Router功能

支持使用URI字符串导航Activity、Fragment、View、RouterHandler,支持正则表达式、回调式onActivityResult、拦截器;

 

RouterHandler还支持异步完成(不阻塞)、指定执行线程等等;

 

同时针对Fragment,支持单Page、栈Page、ViewPager三种形式的Fragment加载。

3 强大的ServiceLoader能力

DRouter同样是基于SPI的理念,路由表会生成接口或基类对实现类的映射。

 

  • 获取实例时可以指定执行任意构造器、单例、优先级排序、自动拆解所有接口和基类作为key

  • 可以通过alias,以及任意数量多的维度对目标进行多维过滤

  • 动态注册

4 像调用本地方法一样跨进程通信和回调

无需编写繁琐的aidl文件实现跨进程调用,使用方式几乎等同本地导航RouterHandler和Service,只需增加一些配置即可。

 

  • 不需要异步去bindService等待,同步执行

  • 支持跨应用

  • 替代反射,服务端使用本地方法执行,提高执行效率

  • 支持任意类型的对象跨进程传递,包括Context、自定义类,支持RemoteCallback回调

  • 服务端异常崩溃重启后,客户端按需自动重新执行已发送的跨进程命令。

5 框架内部尽可能减少使用反射,提升性能

加载路由表、实例化路由元素、以及跨进程命令到达服务端后的分发这些常规应该使用反射的场景,使用预占位或动态生成代码来替换成java的new创建和显式方式执行,最大限度的去避免反射执行,提高性能。

 

考虑到功能的全面性,使用ServiceLoader时如指定非默认构造函数以及跨进程时传递自定义类,在框架内部会使用到反射,不过可以使用默认构造函数以及对跨进程对象实现Parcelable来避免。

6 动态下载与api匹配的plugin,自升级

很多项目包括DRouter需要搭配gradle插件和java依赖来使用,正常来讲升级java依赖时大概率需要同时升级gradle插件,这在滴滴这种业务线繁多,各业务线除了有自己的组件同时又有自己的壳工程场景是一个非常痛的点。当业务线的组件因平台的同学在公共层升级了java依赖后,但又没有同时手动更新自己业务壳工程的gradle插件,大概率就会编译失败。

 

DRouter利用plugin-proxy壳插件来解决这个问题,壳插件会在编译期自动检查java依赖的版本,同时获得应该匹配的插件版本。接着plugin-proxy会去下载这个匹配的gradle插件,并最终执行。这样就解决了因升级java依赖而gradle插件不匹配导致的编译问题。

7‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 无需手动添加混淆规则

DRouter把混淆规则隐藏到了java依赖里,当启用混淆功能时会自动应用混淆规则。这样即使升级了DRouter版本也无需关心混淆规则是否需要升级。

4. 

DRouter的原理和架构

整体架构分三层,自下而上是数据流层、组件层、开放接口层。

4.1 数据流层

数据流层是DRouter最重要的核心模块,这里承载着插件生成的路由表、路由元素、动态注册、以及跨进程功能相关的序列化数据流。所有的路由流转都会从这里取得对应的数据,进而流向正确的目标。

4.2 组件层

这一层是功能组件层,核心的路由分发、拦截器、生命周期、异步暂存和监控、ServiceLoader、多维过滤、Fragment路由,以及跨进程命令打包等。

4.3 开放接口层

DRouter在接口层做了大量的精简和优化,在灵活性和易用性方面做了很多权衡,主要目的是减少冗余API,使框架更为简单的使用和接入。比如Request和ServiceLoader作为最核心的API方法数非常少,一些不常用的功能会放到Extend中。

RouterFlow

路由数据流从创建Request开始,通过URI在路由表中找出所有的结点,会按照RouterHandler、UI的顺序以及优先级顺序执行。每一个元素都可以定义自己的拦截器,这里的拦截器必须放行以后才能执行对应的结点;同时对于RouterHandler执行完又可以决定是否拦截后面所有的结点。当所有的结点执行完,且异步暂存态也都已释放,最终把结果回传给请求处。

ServiceFlow

ServiceLoader既可以获取Class也可以获取对象实例,核心是路由表和过滤器,其中FeatureMatcher拥有多维过滤的功能。像滴滴这种多业务场景的应用,在使用MVP架构时对P的匹配至少会需要根据所在国家、订单业务线、订单状态三个维度来分别对应唯一的Presenter,多维过滤就很容易解决这个问题。性能方面,对象实例化会根据构造器类型,利用插件生成的RouterProxy代码通过new来实例化无参对象(默认构造)或者反射实例化有参对象(非默认构造)。

RemoteFlow

DRouter的跨进程功能是一大特色,左侧绿色代表客户端,自上而下会把所有的参数打包成命令,这里支持任意类型,框架内有一套完整的机制通过遍历集合、转换、组装,最后存储到Parcel里。利用Authority查找到服务端的Provider并随之利用此通道返回服务端的Binder。

在服务端,也就是右侧的紫色,会自下而上把命令解包和分发。然后利用DRouter的路由RouterHandler和ServiceLoader的功能,使得客户端的命令最终在服务端执行。插件会在服务端生成一段代码,这段代码可以避免使用反射,提高整体的执行效率。

 

整个过程同步执行,使用简单、高效。

5. 

写在最后

DRouter是一套功能完善、定制化程度高的路由框架,具有易于上手、架构清晰、性能优良的特点。现已成为滴滴不可或缺的基础组件之一。DRouter作为组件化方案的践行者,将继续提升各个场景的支持能力,也欢迎各位开发者使用并提出宝贵的反馈意见。

 

GitHub开源地址:

https://github.com/didi/DRouter

开源作者

    

 

延伸阅读


内容编辑 | Hokka联系我们 | [email protected]

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/DiDi_Tech/article/details/115301543

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法