姓名:唐來賓? 學(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)的所有鏈路都替你弄好羞海,你可以留著時間去干其他更重要的事情忌愚,譬如陪陪家人,逗逗狗什么的却邓。