Android 組件化 —— 路由設(shè)計最佳實踐

姓名:唐來賓? 學(xué)號:17101223417

轉(zhuǎn)載https://mp.weixin.qq.com/s/ouiBy2gYIXIN9MTxAMGtmQ

【嵌牛導(dǎo)讀】什么是路由,是為了解決什么問題才產(chǎn)生的哩牍;

業(yè)界現(xiàn)狀是怎么樣的露久,我們可以做什么來優(yōu)化當前的問題;

路由設(shè)計思路是怎么樣的田晚,該怎么設(shè)計比較好凹联;

如何用注解實現(xiàn)路由表滤淳;

URL的參數(shù)如何依賴注入到Activity、Fragement义起;

如何HookOnActivityResult嘲玫,不需要再進行requstCode判斷;

如何異步攔截路由并扇,實現(xiàn)線程切換去团,不阻塞頁面跳轉(zhuǎn);

如何用Apt實現(xiàn)Retrofit接口式調(diào)用;

如何找到Activity的調(diào)用方土陪;

如何實現(xiàn)路由的安全調(diào)用昼汗;

如何避開Apt不能匯總所有Module路由的問題。

【嵌牛鼻子】路由鬼雀,接口問題

【嵌牛提問】如更好的使用路由?

【嵌牛正文】引子

這篇文章會告訴你:

什么是路由顷窒,是為了解決什么問題才產(chǎn)生的;

業(yè)界現(xiàn)狀是怎么樣的源哩,我們可以做什么來優(yōu)化當前的問題鞋吉;

路由設(shè)計思路是怎么樣的,該怎么設(shè)計比較好励烦;

如何用注解實現(xiàn)路由表谓着;

URL的參數(shù)如何依賴注入到Activity、Fragement坛掠;

如何HookOnActivityResult赊锚,不需要再進行requstCode判斷;

如何異步攔截路由屉栓,實現(xiàn)線程切換舷蒲,不阻塞頁面跳轉(zhuǎn);

如何用Apt實現(xiàn)Retrofit接口式調(diào)用友多;

如何找到Activity的調(diào)用方牲平;

如何實現(xiàn)路由的安全調(diào)用;

如何避開Apt不能匯總所有Module路由的問題域滥。

前言

當前Android的路由庫實在太多了欠拾,剛開始的時候想為什么要用路由表的庫,用Android原生的Scheme碼不就好了骗绕,又不像iOS只能類依賴藐窄,后面越深入就越發(fā)現(xiàn)當時想的太簡單了,后面看到Retrofit和OkHttp酬土,才想到頁面請求本質(zhì)和網(wǎng)絡(luò)請求不是一樣嗎荆忍,終于業(yè)界最簡單高效的路由方案1.0出來了。

開源的庫后面會放在公司github地址上面撤缴。

背景

1

什么是路由

根據(jù)路由表將頁面請求分發(fā)到指定頁面刹枉。

2

使用場景

App接收到一個通知,點擊通知打開App的某個頁面

瀏覽器App中點擊某個鏈接打開App的某個頁面

運營活動需求屈呕,動態(tài)把原生的頁面替換成H5頁面

打開頁面需要某些條件微宝,先驗證完條件,再去打開那個頁面

不合法的打開App的頁面被屏蔽掉

H5打開鏈接在所有平臺都一樣虎眨,方便統(tǒng)一跳轉(zhuǎn)

App存在就打開頁面蟋软,不存在就去下載頁面下載,只有Google的App Link支持

3

為什么要有路由

Android原生已經(jīng)支持AndroidManifest去管理App跳轉(zhuǎn)镶摘,為什么要有路由庫,這可能是大部分人接觸到Android各種Router庫不太明白的地方岳守,這里我講一下我的理解

顯示Intent:項目龐大以后凄敢,類依賴耦合太大,不適合組件化拆分

隱式Intent:協(xié)作困難湿痢,調(diào)用時候不知道調(diào)什么參數(shù)

每個注冊了Scheme的Activity都可以直接打開涝缝,有安全風(fēng)險

AndroidMainfest集中式管理比較臃腫

無法動態(tài)修改路由,如果頁面出錯譬重,無法動態(tài)降級

無法動態(tài)攔截跳轉(zhuǎn)拒逮,譬如未登錄的情況下,打開登錄頁面臀规,登錄成功后接著打開剛才想打開的頁面

H5滩援、Android、iOS地址不一樣以现,不利于統(tǒng)一跳轉(zhuǎn)

4

怎么樣的路由才算好路由

路由說到底還是為了解決開發(fā)者遇到的各種奇葩需求狠怨,使用簡單约啊、侵入性低特碳、維護方便是首要條件全蝶,不影響你原來的代碼,寫入代碼也很少,這里就要說說我的OkDeepLink的五大功能了爱沟,五大功能瞬間擊中你的各種痛點,早點下班不是夢嗽仪。

編譯時注解鳄哭,實現(xiàn)靜態(tài)路由表,不再需要在臃腫的AndroidManifest中找到那個Actvity寫Scheme和Intent Filter

異步攔截器,實現(xiàn)動態(tài)路由萎胰,安全攔截碾盟、動態(tài)降級難不倒你

模仿Retrofit接口式調(diào)用,實現(xiàn)方式用apt技竟,不耗性能冰肴,參數(shù)調(diào)用不再是問題

HookOnActivityResult,支持RxJava響應(yīng)式調(diào)用,不再需要進行requestCode判斷

參數(shù)依賴注入,自動保存榔组,不再需要手動寫onSaveInstance熙尉、onCreate(SaveInstace)、onNewIntent(Intent)搓扯、getQueryParamer

注冊路由

路由結(jié)構(gòu)圖

5

詳細比較

大部分路由庫都用Apt(編譯時注解)生成路由表检痰,然后用路由表轉(zhuǎn)發(fā)到指定頁面。

其實說到底锨推,路由的本質(zhì)就是注冊再轉(zhuǎn)發(fā)铅歼,圍繞著轉(zhuǎn)發(fā)可以進行各種操作公壤,攔截,替換谭贪,參數(shù)獲取等等境钟,其他Apt、Rxjava說到底都只是為了方便使用出現(xiàn)的俭识,這里你會發(fā)現(xiàn)各種路由庫反而為了修復(fù)各種工具帶來的問題慨削,出現(xiàn)了原來沒有的問題,譬如DeepLinkDispatch為了解決Apt沒法匯總所有Module路由套媚,每個module都要手動注冊缚态,ARouter為了解決Apt沒法匯總所有Module路由,通過類操作耗時堤瘤,才出現(xiàn)分組的概念玫芦。

原理分析

原理流程圖

1

定義路由

路由定義

我這邊是完全按照URL規(guī)范了,這里要說一下本辐,現(xiàn)在好多方法是把參數(shù)定義在path里面的桥帆,雖然這樣做,有不需要額外傳參數(shù)的好處慎皱,但是這樣路由就沒有那么靈活老虫,調(diào)試起來就沒有那么方便了。

建議有好幾款app的公司茫多,host都一樣祈匙,只有scheme不一樣,這樣只要替換Scheme就能實現(xiàn)降級天揖,維護也簡單夺欲。

2

路由注冊

AndroidManifest里面的acitivity聲明scheme碼是不安全的,所有App都可以打開這個頁面今膊,這里就產(chǎn)生有兩種方式去注冊些阅,

注解產(chǎn)生路由表,通過DispatchActivity轉(zhuǎn)發(fā)

AndroidManifest注冊斑唬,將其export=fasle市埋,但是再通過DispatchActivity轉(zhuǎn)發(fā)Intent,天貓就是這么做的赖钞,比上面的方法的好處是路由查找都是系統(tǒng)調(diào)用腰素,省掉了維護路由表的過程,但是AndroidManifest配置還是比較不方便的

我現(xiàn)在還是采用了注解雪营,后面我會結(jié)合兩種方法弓千,將注解自動修改AndroidManifest,對于接入方是沒有變動的献起,方法已經(jīng)找到了洋访,用自定義Lint掃描出注解相關(guān)的Activity镣陕,然后用processManifestTask修改Manifest,有個demo了姻政,后面會接入呆抑。

3

生成路由表

譬如通過Apt把這段代碼

生成

4

初始化路由表

這里就要提一下使用Apt會造成每個module都要手動注冊

DeepLinkDispatch是這么做的

ARouter是通過類查找,就比較耗時了,所以他又加入了分組的概念汁展,按需加載

ActivityRouter就比較巧妙了鹊碍,通過Stub項目,其他地方都是provide的食绿,只有主工程里面用Apt生成RouterInit類,雖然還是要寫module的注解

天貓 統(tǒng)跳協(xié)議 是最簡單的侈咕,轉(zhuǎn)發(fā)一下Intent就可以,但是這樣就沒法享受注解的好處了器紧。

而我用aspectj解決了這個問題耀销,會自動匯總所有module的路由省略了這些多余的代碼,或者有誰知道用Apt自生怎么解決铲汪,請聯(lián)系我一下熊尉。

5

路由查找

路由查找就是查找路由表對應(yīng)的頁面,值得提起的就是因為要適應(yīng)Module接入不同App掌腰,Scheme要自動適應(yīng)狰住,路由表其實是Path---》Activity,這樣的話內(nèi)部跳轉(zhuǎn)的時候ARouterUri是沒有的辅斟。而我這邊是有的转晰,我組裝了一個內(nèi)部的Uri芦拿,這樣攔截器不會有影響士飒。

6

路由分發(fā)

現(xiàn)在所有路由方案分發(fā)都是用Activity做分發(fā)的,這樣做會有這幾個缺點

每次都要啟動一個Activity蔗崎,而Activity就算不寫任何代碼啟動都要0.1秒

如果是異步等待的話酵幕,Activiy要在合適時間finish,不然會有一層透明的頁面阻擋操作

對于第一個問題缓苛,有兩個方法

QQ音樂是把DispatchActivity設(shè)為SingleInstacne,但是這樣的話芳撒,動畫會奇怪,堆棧也會亂掉未桥,后退會有一層透明的頁面阻擋操作

DispatchActivity只在外部打開的時候調(diào)用

我選擇了第二種

對于第二個問題笔刹,有兩個方法

DispatchActivity再把Intent轉(zhuǎn)發(fā)到Service,再finish,這種方法唯一的缺陷是攔截器里面的context是Servcie的activity冬耿,就沒發(fā)再攔截器里面彈出對話框了舌菜。

DispatchActivity在打開和錯誤的時候finish,如果activity已經(jīng)finish了,就用application的context去轉(zhuǎn)發(fā)路由

我選擇了第二種

其實處理透明Activity阻擋操作可以采用取消所有事件變成無感頁面的方法,但是還是覺得會影響activity堆棧沒有采用這種方案

7

結(jié)果返回

這里我封裝了一個庫RxActivityResult去捕獲onActivityResult亦镶,這樣能保正流式調(diào)用

譬如拍照可以這樣寫,先定義一個接口

然后這樣調(diào)用

是不是很簡單日月,原理是這樣的袱瓮,通過封裝一個RxResultHoldFragment去處理onActivityResult

8

動態(tài)攔截

攔截器是重中之重,有了攔截器可以做好多事情爱咬,可以說之所以要做頁面路由尺借,就是為了要實現(xiàn)攔截器。ARouter是用線程等待實現(xiàn)的精拟,但是現(xiàn)在有Rxjava了燎斩,可以實現(xiàn)更優(yōu)美的方式。

先來看一下我做的攔截器的效果.

是不是很簡單蜂绎,參考了部分OkHttp的實現(xiàn)思路瘫里,加入Rxjava,實現(xiàn)異步攔截荡碾。

首先將請求轉(zhuǎn)換成責(zé)任鏈模式RealCallChain,RealCallChain的call方法實際不會執(zhí)行路由跳轉(zhuǎn),只有Interceptor里面調(diào)用了call.proceed或者call.cancel才會執(zhí)行.

接著處理異步的問題谨读,這里用到了Rxjava的AsyncSubject和BehaviorSubject,

AsyncSubject具有僅釋放Observable釋放的最后一個數(shù)據(jù)的特性坛吁,作為路由請求的發(fā)送器

BehaviorSubject具有一開始就會釋放最近釋放的數(shù)據(jù)的特性劳殖,作為路由攔截器的發(fā)送器

具體實現(xiàn)看核心代碼

9

方法調(diào)用

大部分路由庫都是手動拼參數(shù)調(diào)用路由的,這里模仿了Retrofit接口式調(diào)用拨脉,受了LiteRouter的啟發(fā)哆姻,不過Retrofit使用了動態(tài)代理,我使用的Apt沒有性能損耗玫膀。

通過Apt生成每個接口的實際方法

譬如把SecondService接口

生成

然后調(diào)用

SecondService就生成了矛缨。

為了調(diào)用方便,直接在Activity或者fragement寫這段代碼帖旨,sampleServive就自動生成了

但是如果用到MVP模式箕昭,不是在Activity里面調(diào)用路由,后面會支持在這些類里面自動注入SampleService解阅,現(xiàn)在先用java代碼build

10

參數(shù)獲取

大部分路由庫都是手動獲取參數(shù)的落竹,這樣還要傳入?yún)?shù)key比較麻煩,這里模仿了ARouter,不過我支持類型更全一些,支持Bundle支持的所有類型货抄,而且不需要在Acitivty的onCreate調(diào)用獲取代碼述召。

通過Apt把這段代碼

生成

11

Module接入不同App

這里是參考ARouter把path作為key對應(yīng)activity,這樣接入到其他app中蟹地,就自動替換了scheme碼了

安全

現(xiàn)在有好多人用腳本來打開App积暖,然后干壞事,其實時可以用路由來屏蔽掉.

有三種方法供君選擇怪与,不同方法適合不同場景

1

簽名屏蔽

就是把所有參數(shù)加密成一個數(shù)據(jù)作為sign參數(shù)夺刑,然后比對校驗,但是這要求加密方法不變琼梆,要不然升級了以前的app就打不開了

2

adb打開屏蔽

在android5.1手機上性誉,用adb打開的app它的mReferrer為空

3

包名過濾

這三種方法窿吩,比較適合的還是簽名校驗為主,adb過濾為副

4

如何解決路由造成的Activity堆棧錯亂的問題

activity的launchMode使用不當會照成閃屏頁面打開多次的問題错览,可以參考我這篇文章纫雁。

未來展望

路由是一個基礎(chǔ)模塊,技術(shù)難度雖然不是很大倾哺,但是如果每個開發(fā)都重新踩一遍轧邪,性價比就比較低,我希望能把路由相關(guān)的所有鏈路都替你弄好羞海,你可以留著時間去干其他更重要的事情忌愚,譬如陪陪家人,逗逗狗什么的却邓。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硕糊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子腊徙,更是在濱河造成了極大的恐慌简十,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撬腾,死亡現(xiàn)場離奇詭異螟蝙,居然都是意外死亡,警方通過查閱死者的電腦和手機民傻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門胰默,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漓踢,你說我怎么就攤上這事牵署。” “怎么了彭雾?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵碟刺,是天一觀的道長锁保。 經(jīng)常有香客問我薯酝,道長,這世上最難降的妖魔是什么爽柒? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任吴菠,我火速辦了婚禮,結(jié)果婚禮上浩村,老公的妹妹穿的比我還像新娘做葵。我一直安慰自己,他們只是感情好心墅,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布酿矢。 她就那樣靜靜地躺著榨乎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘫筐。 梳的紋絲不亂的頭發(fā)上蜜暑,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音策肝,去河邊找鬼肛捍。 笑死,一個胖子當著我的面吹牛之众,可吹牛的內(nèi)容都是我干的拙毫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼棺禾,長吁一口氣:“原來是場噩夢啊……” “哼缀蹄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膘婶,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤袍患,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竣付,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诡延,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年古胆,在試婚紗的時候發(fā)現(xiàn)自己被綠了肆良。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逸绎,死狀恐怖惹恃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棺牧,我是刑警寧澤巫糙,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站参淹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏乏悄。R本人自食惡果不足惜浙值,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望檩小。 院中可真熱鬧开呐,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓦戚,卻和暖如春鸵荠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伤极。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工蛹找, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哨坪。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓庸疾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親当编。 傳聞我的和親對象是個殘疾皇子届慈,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

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

  • 引子 這篇文章會告訴你 什么是路由,是為了解決什么問題才產(chǎn)生的 業(yè)界現(xiàn)狀是怎么樣的忿偷,我們可以做什么來優(yōu)化當前的問題...
    信念著了火閱讀 35,403評論 50 237
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,095評論 25 707
  • 我認為做女人一定要獨立金顿、自強。否則走向社會鲤桥,永遠被別人瞧不起或者永遠聽從別人的使喚揍拆。在家庭中,女人若不獨立茶凳、內(nèi)心強...
    心愿_f508閱讀 124評論 0 0
  • 愛與美食的承諾嫂拴,懂比吃更重要 有一批用戶說最幸福的事兒就是Q譜 Q譜來源于全國當?shù)刈畹氐赖奈独賳⒚?Q譜源于小時候...
    Q同學(xué)會閱讀 133評論 0 0
  • 你恰逢我的野蠻與荒唐 卻錯過我的溫柔和堅強 沒有了你之后 自己活成了你的樣子
    S_Cat臭臭貓閱讀 353評論 2 6