? ? ? ? 最近項目中越來越多使用組件化,所以記錄一下由此產生的最基本的一個問題:路由。
? ? ? ? ?安卓頁面間的跳轉想必是每一個學習安卓之人最先接觸的形病,最基本的方式就是直接調用startActivity方法紧阔。這種方法在以前整個工程都是單模塊的時候沒啥大問題,簡單也沒有必要去重新寫新的方法烤黍。但是隨著目前安卓開發(fā)趨向于模塊化組件化知市,如果想用這種方式實現(xiàn)組件間的跳轉,勢必會造成各個組件相互依賴速蕊,造成高耦合度嫂丙,這就違背了組件化非常重要的一個目的:解耦。這樣下來組件就只能適用于當前工程规哲,隨著項目頁面越來越多跟啤,本來作為組件的那部分卻移入了越來越多項目中的依賴,這時如果另一個工程想移入這個組件唉锌,估計開發(fā)人員要炸了隅肥,因為每次移入組件,都會有一堆報紅袄简,因為這個組件還有很大一部分對原來工程里的邏輯腥放。
? ? ? ? 解決思路肯定是解耦,首先想到利用利用scheme進行跳轉痘番,統(tǒng)一一個Activity,內部根據(jù)Uri獲取到domain,再進行統(tǒng)一判斷進行跳轉捉片,但是這樣不夠通用,每個activity都得配置一個判斷汞舱,換一個工程我們又得重新配置一遍伍纫,所以不夠完美。這樣行不通那利用反射機制呢昂芜?每個跳轉都得配置包名跟類名莹规,這個組件換個工程我們得把所有類名更換一遍,還不如上一個方法泌神。良漱。舞虱。。
? ? ? ? 我們想要的是不用每個activity都配置一遍跳轉的邏輯母市,只需要根據(jù)一個參數(shù)就能自動給我們配置好跳轉矾兜,這樣耦合性相當?shù)土恕D且陨线@些都行不通的話患久,有沒有現(xiàn)成的解決方案呢椅寺?
? ? ? ? 答案是當然有,阿里給我們提供了一個ARouter框架蒋失,耦合性低返帕,適合多模塊,支持攔截跟定制篙挽,路由也只需提供一個注解荆萤,無需自定義邏輯,完美解決了上述問題铣卡。使用起來也非常簡單链韭,就不做記錄了儒拂,搜一下去github上阿里寫了詳細的使用說明跟api用法。這里我們需要了解的是雾家,ARouter是如何提供了一套簡單的實現(xiàn)站宗,解決了上述問題,本人借鑒了部分源碼鉴腻,將基礎邏輯剝離了出來,整理成了個簡單的路由框架。
? ? ? ? 首先要想了解ARouter原理量淌,你需要先熟悉以下幾個知識基礎:1.注解跟注解處理器,2.Java apt技術嫌褪,3.JavaPoet動態(tài)依賴注入框架(這個比較重要呀枢,其他幾個帶注解的框架如EventBus,ButterKnife的基本原理也是這個框架)笼痛。這些就不做介紹了裙秋,不然又是個長篇大論。
我們根據(jù)使用ARouter步驟的順序開始整理思路缨伊。
首先跟ARouter一樣摘刑,先創(chuàng)建三個模塊,兩個java library跟一個android library:
分別是注解模塊刻坊,api模塊跟注解處理器模塊枷恕。
一:注解
先從注解模塊開始,我們先定義注冊路由地址的注解:
這里設置成對類注解谭胚,并設置成編譯時注解徐块,比較簡單未玻。
一:注解處理器
定義完注解我們需要處理注解處理器。先添加好依賴胡控,引入注解模塊扳剿,javapoet跟谷歌的auto service主動注冊處理器框架:
定義注解處理器RouterProcessor:
在上面添加自動注冊的注解,接下來需要實現(xiàn)注解處理器的幾個方法:
這里process方法中核心代碼集中在creatJavaCode中癣猾,該方法的作用是獲取注解信息生成java文件敛劝,生成類添加了方法,配置了包名跟實現(xiàn)的接口纷宇,我們看下代碼:
這里根據(jù)拿到的注解外部類的信息編譯時自動生成代碼夸盟,代碼的作用是將注解的外部類的路由信息跟路徑保存在一個map中。
我們看看生成的代碼像捶,跟我們構建代碼部分設置的一樣上陕,存儲的類實現(xiàn)了IRouterZ接口(到后面就知道為什么要專門實現(xiàn)一個接口而不只是添加方法了):
這樣一來最核心的路由信息存儲部分就完成了,它為我們省去了每個activity配置跳轉信息的操作拓春,不得不感慨javapoet這個功能是真的強大释簿。。硼莽。
三.讀取
接下來跳轉頁面我們就根據(jù)存下來的路由map進行跳轉庶溶,那么問題來了,編譯完之后我們如何從動態(tài)生成的代碼中獲取到路由信息呢懂鸵?
ARouter的思路是在初始化的時候就從生成的所有dex中去尋找實現(xiàn)了IRouterZ的類偏螺,這部分類即是動態(tài)構建代碼時生成的,然后傳一個相同的map進去依次執(zhí)行所有的onLoad方法匆光,這樣所有的路由信息就全部都保存到了我們自己創(chuàng)建的map中去套像,思路有了我們看看具體代碼:
既然是在初始化時候實現(xiàn)那肯定是Init方法作為入口:
init方法沒啥信息,我們看LogisticsCenter.init(context)里面執(zhí)行了什么:
再順勢跳到registerComm里面:
可以看出终息,這個方法從生成代碼時候配置的包名里面遍歷所有的class夺巩,找到實現(xiàn)了IRouterZ的類,最后執(zhí)行onLoad方法周崭,將路由信息存入Warehouse.routeMap中去柳譬,Warehouse.routeMap是個存放path跟路由信息的map:
三.路由跳轉
好了,最關鍵的配置休傍,存儲跟讀取都實現(xiàn)了征绎,最后一步的跳轉其實相對來說就很簡單了,因為在存儲的時候我們獲取到注解的外部類,比如activity人柿,fragment,獲取到了這些類其實最后就用最基本的startActivity就行了柴墩,當然,路由信息的結構在寫框架時你可以自己定義凫岖,所以跳轉前判斷邏輯也可能不盡相同江咳,這里我們還是參照ARouter寫,以activity為例:
首先activity中調用:
我們依次看這幾個方法,先看build:
可以看出build方法實際上是根據(jù)地址new了一個PostCard對象哥放,PostCard繼承了路由信息所在的類歼指,因此在這里它當做路由信息來用。
bundle方法實際上就是個set方法甥雕,將Bundle數(shù)據(jù)傳給postcard對象踩身。
最后關鍵的navigation方法肯定是跳轉了,我們看下怎么實現(xiàn)的:
跳到ZRouter的navigation方法中去:
因為最開始Postcard只有path被賦值了社露,單單靠這個無法完成跳轉挟阻,那么一直低調不被注意的Warehouse.routeMap終于派上用場了,這個我們把它稱之為路由表峭弟,在LogisticsCenter.complete(postcard);方法中附鸽,我們根據(jù)路由表完善了Postcard的各個參數(shù),我們看看:
setDestination設置了目標類瞒瘸,setRouteType設置了跳轉的類型坷备,這里是activity,因此根據(jù)上面代碼執(zhí)行的是startActivity(postcard)方法情臭,再看這個方法:
這部分就很簡單了省撑,設置啟動模式,設置intent傳參谎柄,最后在主線程運行startActivity完成跳轉丁侄。
由此惯雳,ARouter的基本功能也就實現(xiàn)了朝巫,看到最后可以總結出,歸根結底還是運用的startActivity石景,只不過判斷信息都巧妙的存放在了路由表里而已劈猿。