滴滴開源DRouter:一款高效的Android路由框架

導讀


DRouter是滴滴乘客端自研的一套Android路由框架赋访,基于平臺化解耦的思想,為組件間通信服務缓待。該項目以功能全面蚓耽、易用為原則,支持各種路由場景旋炒,在頁面路由步悠、服務獲取和過濾、跨進程及跨應用瘫镇、VirtualApk插件支持等方面都能提供多樣化的服務鼎兽。目前已在滴滴乘客端、順風車铣除、單車谚咬、國際化、滴滴定制車中控尚粘、滴滴車載屏等十多個滴滴的app內(nèi)使用择卦,得到各種場景的驗證。

1.
DRouter簡介


隨著業(yè)務不斷的發(fā)展郎嫁,業(yè)務變的種類繁多秉继,項目代碼集中且耦合在一起,導致編譯速度慢泽铛、開發(fā)效率低秕噪、維護困難、牽一發(fā)動全身厚宰。這時無論是從工程效能還是業(yè)務迭代穩(wěn)定性的角度考慮,都需要把項目代碼拆分成獨立的組件遂填。當組件拆分后铲觉,在沒有代碼耦合的前提下,如何快速吓坚、方便撵幽、靈活的進行組件間跳轉(zhuǎn)、數(shù)據(jù)通信礁击、生命周期控制是DRouter要解決的問題盐杂。

支持的功能

  • 使用URI字符串導航Activity逗载、Fragment、View链烈、RouterHandler
  • 路由注解支持正則表達式厉斟、占位符
  • 回調(diào)式onActivityResult
  • RouterHandler、Activity支持等待異步完成(hold)强衡,并可設(shè)置超時時間
  • RouterHandler指定執(zhí)行線程
  • 注入攔截器擦秽,支持全局攔截器和局部攔截器,面向切面編程
  • 更為多樣化的Fragment頁面跳轉(zhuǎn)能力
  • 使用接口或基類導航到實現(xiàn)類Service的Class以及實例
  • 支持Service別名以及多維過濾器查找
  • 導航Service可指定任意構(gòu)造器漩勤、支持單例
  • 支持動態(tài)注冊RouterHandler感挥、Service,綁定生命周期自動解綁
  • 簡單易用的跨進程執(zhí)行RouterHandler越败、Service
  • 跨進程訪問無需提前綁定触幼、如同本地調(diào)用一樣進行訪問
  • 支持客戶端進程和服務端進程自動重連
  • 支持VirtualApk

2.
為什么要開發(fā)DRouter


滴滴乘客端包容萬象,擁有眾多的業(yè)務和組件究飞,早在15年就已經(jīng)啟動組件化重構(gòu)置谦。使用滴滴自研的基于JavaSPI插件技術(shù),動態(tài)的生成從接口到實現(xiàn)的映射關(guān)系噪猾,完成了組件化的通信功能霉祸,并且應用到了整個滴滴的Android體系中。雖然SPI性能優(yōu)良袱蜡、使用便捷丝蹭,但同時功能單一、支持的場景少也是一直存在的問題坪蚁。隨著組件化實踐的常態(tài)化奔穿,急需要一款功能強大,適合滴滴的場景敏晤,定制化程度高的路由框架贱田。

目前市面上已經(jīng)存在幾款路由框架了,比如ARouter嘴脾,WMRouter男摧。不過滴滴業(yè)務繁多,平臺化遍布各種場景译打,對路由的定制化需求強烈耗拓,很多功能市面上的路由是不支持的,比如:

  • ARouter作為路由的先行者奏司,由于開發(fā)較早功能簡單乔询,路由查找過程性能損耗較大
  • WMRouter在路由的性能上仍然有一定優(yōu)化空間,沒有導航到Fragment/View韵洋、回調(diào)式ActivityResult竿刁,使用ServiceLoader稍顯繁瑣且無法動態(tài)注冊以及多維過濾
  • 現(xiàn)今沒有一款路由框架對完整的跨進程和跨應用有較好的支持黄锤,這在滴滴定制車團隊有著強訴求
  • 同時沒有一款路由對Fragment提供更多的擴展能力,DRouter在鴻鵠和車載屏項目以及更多Fragment的場景可以提供支持

基于以上問題食拜,從2018年開始自研了DRouer路由框架鸵熟,為滴滴的平臺化服務幾十種場景。

3.
DRouter有哪些亮點


3.1 插件增量編譯监婶、多線程掃描旅赢,運行時異步加載路由表

路由表在編譯期通過插件動態(tài)生成。插件會啟動多線程同時異步處理所有的組件惑惶;增量掃描功能可以幫助開發(fā)者在第二次編譯時煮盼,只對修改過的代碼進行處理,極大地縮短路由表生成的時間带污。

在本人的開發(fā)機上測試僵控,19年滴滴乘客端掃描5.5萬個類,全量需要不到6s的時間鱼冀;如果是修改了application模塊报破,增量掃描只需要處理修改的單類,耗時0.4s時間千绪;如果是修改了library組件充易,增量掃描需要掃描整個jar包,根據(jù)jar包的大小時間會稍多一些荸型,例如滴滴岉镅ィ快組件增量耗時3.9s。

另外框架初始化的時候啟動子線程去加載路由表瑞妇,不阻塞主線程的執(zhí)行稿静,盡其所能提高效率。

3.2 完整的Router功能

支持使用URI字符串導航Activity辕狰、Fragment改备、View、RouterHandler蔓倍,支持優(yōu)先級悬钳、正則表達式、占位符偶翅、回調(diào)式onActivityResult他去、攔截器;RouterHandler還支持異步完成(不阻塞)倒堕、指定執(zhí)行線程等等;同時針對Fragment爆价,支持單Page垦巴、棧Page媳搪、ViewPager三種形式的Fragment加載。

3.3 強大的ServiceLoader能力

DRouter同樣是基于SPI的理念骤宣,路由表會生成接口或基類對實現(xiàn)類的映射秦爆。

  • 獲取實例時可以指定執(zhí)行任意構(gòu)造器、單例憔披、優(yōu)先級排序等限、自動拆解所有接口和基類作為key
  • 可以通過alias,以及任意數(shù)量多的維度對目標進行過濾
  • 動態(tài)注冊

3.4 像調(diào)用本地方法一樣進行跨進程通信和回調(diào)

無需編寫繁瑣的aidl文件實現(xiàn)跨進程調(diào)用芬膝,使用方式幾乎等同本地導航RouterHandler和Service望门,只需增加一些配置即可。

  • 不需要異步去bindService等待锰霜,同步執(zhí)行
  • 支持跨應用
  • 替代反射筹误,服務端使用本地方法執(zhí)行,提高執(zhí)行效率
  • 支持任意類型的對象跨進程傳遞癣缅,包括Context厨剪、自定義類,支持RemoteCallback回調(diào)
  • 服務端異常崩潰重啟后友存,客戶端按需自動重新執(zhí)行已發(fā)送的跨進程命令

3.5 框架內(nèi)部盡可能減少使用反射祷膳,提升性能

加載路由表、實例化路由屡立、以及跨進程命令到達服務端后的分發(fā)這些常規(guī)應該使用反射的場景直晨,使用預占位或動態(tài)生成代碼來替換成java的new創(chuàng)建和顯式方式執(zhí)行,最大限度的去避免反射執(zhí)行侠驯,提高性能抡秆。

考慮到功能的全面性,使用ServiceLoader時如指定非默認構(gòu)造函數(shù)以及跨進程時傳遞自定義類吟策,在框架內(nèi)部會使用到反射儒士,不過可以使用默認構(gòu)造函數(shù)以及對跨進程對象實現(xiàn)Parcelable來避免。

3.6 動態(tài)下載與api匹配的plugin檩坚,插件自升級

很多項目包括DRouter需要搭配gradle插件和java依賴來使用着撩,正常來講升級java依賴時大概率需要同時升級gradle插件,這在滴滴這種業(yè)務線繁多匾委,各業(yè)務線除了有自己的組件同時又有自己的殼工程場景是一個非常痛的點拖叙。當業(yè)務線的組件因平臺的同學在公共層升級了java依賴后,但又沒有同時手動更新自己業(yè)務殼工程的gradle插件赂乐,大概率就會編譯失敗薯鳍。

DRouter利用plugin-proxy殼插件來解決這個問題,殼插件會在編譯期自動檢查java依賴的版本挨措,同時獲得應該匹配的插件版本挖滤。接著plugin-proxy會去下載這個匹配的gradle插件崩溪,并最終執(zhí)行。這樣就解決了因升級java依賴而gradle插件不匹配導致的編譯問題斩松。

3.7 無需手動添加混淆規(guī)則

DRouter把混淆規(guī)則隱藏到了java依賴里伶唯,當啟用混淆功能時會自動應用混淆規(guī)則。這樣即使升級了DRouter版本也無需關(guān)心混淆規(guī)則是否需要升級惧盹。

4.
DRouter的原理和架構(gòu)


DRouter_Architecture.jpg

架構(gòu)設(shè)計

整體架構(gòu)分三層乳幸,自下而上是數(shù)據(jù)流層、組件層钧椰、開放接口層粹断。

4.1 數(shù)據(jù)流層

數(shù)據(jù)流層是DRouter最重要的核心模塊,這里承載著插件生成的路由表演侯、路由元素姿染、動態(tài)注冊、以及跨進程功能相關(guān)的序列化數(shù)據(jù)流秒际。所有的路由流轉(zhuǎn)都會從這里取得對應的數(shù)據(jù)悬赏,進而流向正確的目標。

4.2 組件層

這一層是功能組件層娄徊,核心的路由分發(fā)闽颇、攔截器、生命周期寄锐、異步暫存和監(jiān)控兵多、ServiceLoader、多維過濾橄仆、Fragment路由剩膘,以及跨進程命令打包等。

4.3 開放接口層

DRouter在接口層做了大量的精簡和優(yōu)化盆顾,在靈活性和易用性方面做了很多權(quán)衡怠褐,主要目的是減少冗余API,使框架更為簡單的使用和接入您宪。比如Request和ServiceLoader作為最核心的API方法數(shù)非常少奈懒,一些不常用的功能會放到Extend中。

功能原理

RouterFlow

Router_Flow.jpg

路由數(shù)據(jù)流從創(chuàng)建Request開始宪巨,通過URI在路由表中找出所有的結(jié)點磷杏,會按照RouterHandler、UI的順序以及優(yōu)先級順序執(zhí)行捏卓。每一個元素都可以定義自己的攔截器极祸,這里的攔截器必須放行以后才能執(zhí)行對應的結(jié)點;同時對于RouterHandler執(zhí)行完又可以決定是否攔截后面所有的結(jié)點。當所有的結(jié)點執(zhí)行完遥金,且異步暫存態(tài)也都已釋放峦椰,最終把結(jié)果回傳給請求處。

ServiceFlow

Service_Flow.jpg

ServiceLoader既可以獲取Class也可以獲取對象實例汰规,核心是路由表和過濾器,其中FeatureMatcher擁有多維過濾的功能物邑。像滴滴這種多業(yè)務場景的應用溜哮,在使用MVP架構(gòu)時對P的匹配至少會需要根據(jù)所在國家、訂單業(yè)務線色解、訂單狀態(tài)三個維度來分別對應唯一的Presenter茂嗓,多維過濾就很容易解決這個問題。性能方面科阎,對象實例化會根據(jù)構(gòu)造器類型述吸,利用插件生成的RouterProxy代碼通過new來實例化無參對象(默認構(gòu)造)或者反射實例化有參對象(非默認構(gòu)造)。

RemoteFlow

Remote_Flow.jpg

DRouter的跨進程功能是一大特色锣笨,左側(cè)綠色代表客戶端蝌矛,自上而下會把所有的參數(shù)打包成命令,這里支持任意類型错英,框架內(nèi)有一套完整的機制通過遍歷集合入撒、轉(zhuǎn)換、組裝椭岩,最后存儲到Parcel里茅逮。利用Authority查找到服務端的Provider并隨之利用此通道返回服務端的Binder。

在服務端判哥,也就是右側(cè)的紫色献雅,會自下而上把命令解包和分發(fā)。然后利用DRouter的路由RouterHandler和ServiceLoader的功能塌计,使得客戶端的命令最終在服務端執(zhí)行挺身。插件會在服務端生成一段代碼,這段代碼可以避免使用反射夺荒,提高整體的執(zhí)行效率瞒渠。

整個過程同步執(zhí)行,使用簡單技扼、高效伍玖。

5.
寫在最后


DRouter是一套功能完善、定制化程度高的路由框架剿吻,具有易于上手窍箍、架構(gòu)清晰、性能優(yōu)良的特點。現(xiàn)已成為滴滴不可或缺的基礎(chǔ)組件之一椰棘。

統(tǒng)計數(shù)據(jù)看纺棺,從2020年底開源至今公司內(nèi)外目前已有62個不同的app接入DRouter,在此感謝各位的信任和支持邪狞。DRouter作為組件化方案的踐行者祷蝌,將繼續(xù)提升各個場景的支持能力,并會持續(xù)進行問題修復以及功能迭代帆卓,也歡迎各位開發(fā)者使用并提出寶貴的反饋意見巨朦。

6.
微信交流群


成長的過程很艱辛,但我們每天都在努力

歡迎各位Android開發(fā)者加入 DRouter微信技術(shù)交流群

7.
使用文檔


GitHub開源地址 ??https://github.com/didi/DRouter
接入和使用文檔 ????https://github.com/didi/DRouter/wiki

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剑令,一起剝皮案震驚了整個濱河市糊啡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吁津,老刑警劉巖棚蓄,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碍脏,居然都是意外死亡梭依,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門潮酒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睛挚,“玉大人,你說我怎么就攤上這事急黎≡” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵勃教,是天一觀的道長淤击。 經(jīng)常有香客問我,道長故源,這世上最難降的妖魔是什么污抬? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮绳军,結(jié)果婚禮上印机,老公的妹妹穿的比我還像新娘。我一直安慰自己门驾,他們只是感情好射赛,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奶是,像睡著了一般楣责。 火紅的嫁衣襯著肌膚如雪竣灌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天秆麸,我揣著相機與錄音初嘹,去河邊找鬼。 笑死沮趣,一個胖子當著我的面吹牛屯烦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播房铭,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼漫贞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了育叁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芍殖,失蹤者是張志新(化名)和其女友劉穎豪嗽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豌骏,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡龟梦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了窃躲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片计贰。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蒂窒,靈堂內(nèi)的尸體忽然破棺而出躁倒,到底是詐尸還是另有隱情,我是刑警寧澤洒琢,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布秧秉,位于F島的核電站,受9級特大地震影響衰抑,放射性物質(zhì)發(fā)生泄漏象迎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一呛踊、第九天 我趴在偏房一處隱蔽的房頂上張望砾淌。 院中可真熱鬧,春花似錦谭网、人聲如沸汪厨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骄崩。三九已至聘鳞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間要拂,已是汗流浹背抠璃。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脱惰,地道東北人搏嗡。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像拉一,于是被迫代替她去往敵國和親采盒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容