組件化
模塊化绰垂、組件化與插件化
在項(xiàng)目發(fā)展到一定程度,隨著人員的增多火焰,代碼越來越臃腫劲装,這時(shí)候就必須進(jìn)行模塊化的拆分。在我看來荐健,模塊化是一種指導(dǎo)理念酱畅,其核心思想就是分而治之、降低耦合江场。而在Android工程中如何實(shí)施纺酸,目前有兩種途徑,也是兩大流派址否,一個(gè)是組件化餐蔬,一個(gè)是插件化。
既然組件化和插件化都是為了模塊化而生的佑附,那么他們有什么區(qū)別樊诺,我覺得最大的區(qū)別應(yīng)該就是動(dòng)態(tài)修改的能力,這里的動(dòng)態(tài)修改指的是運(yùn)行期的動(dòng)態(tài)修改音同,插件化顯然是可以支持的词爬,但是組件化卻不行,它只允許編譯期的動(dòng)態(tài)修改权均。
所以為什么要做的是組件化而不是插件化顿膨,作為RD我覺得理由大概如下吧
插件化有很多坑要躺——插件化框架本身的不穩(wěn)定讓開發(fā)者前赴后繼的躺坑
發(fā)不完的版本——插件化可以運(yùn)行時(shí)修改,PM表示非常完美叽赊,RD變身成真業(yè)務(wù)搬磚工
組件化沒有黑科技恋沃,穩(wěn)定——原生能力支持這種靈活配置的方式
組件化工作
代碼解耦
一個(gè)比較理想的解耦狀態(tài)應(yīng)當(dāng)是使用AndroidStudio提供的multiple module能力將主項(xiàng)目中的已有模塊進(jìn)行拆分,這里的module我們分為兩種
一種是基礎(chǔ)庫library必指,這些代碼可以直接被其他模塊直接引用囊咏,比如網(wǎng)絡(luò)庫,我們稱之為library。另一種是一個(gè)完整的功能模塊梅割,比如會(huì)員中心霜第,我們稱之為Component。拆分后的結(jié)果應(yīng)該是類似于如下樣式
那么解耦到什么樣的效果户辞,才是我們需要的呢庶诡,顯然主模塊以及各個(gè)Component之間不允許有直接的引用,我們解耦的主要目標(biāo)就是要做到完全隔離的效果咆课,不能直接使用其他Component內(nèi)的類并且最好不了解其中的實(shí)現(xiàn)細(xì)節(jié)。
組件的單獨(dú)調(diào)試
其實(shí)單獨(dú)調(diào)試比較簡單扯俱,只需要把a(bǔ)pply plugin: ‘com.android.library’切換成apply plugin: ‘com.android.application’就可以书蚪,但是我們還需要修改一下AndroidManifest文件,因?yàn)橐粋€(gè)單獨(dú)調(diào)試需要有一個(gè)入口的Actiivity迅栅。具體如下
在gradle.properities配置中放入如下參數(shù)
##### 是否單獨(dú)調(diào)試A模塊 #####
DEBUG_MODULE_A=false
##### 主模塊是否需要引入A模塊 #####
NEED_MODULE_A=true
在業(yè)務(wù)module的build.gradle中添加如下代碼
if (DEBUG_MODULE_A.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
// ...
sourceSets {
main {
if (DEBUG_MODULE_A.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
在主module的build.gradle中添加如下代碼
// a模塊非debug且需要打包a模塊能力時(shí)殊校,才包含a模塊
if (!DEBUG_MODULE_A.toBoolean() && NEED_MODULE_A.toBoolean()) {
implementation project(':module-a')
}
在業(yè)務(wù)module的src文件夾下添加對(duì)應(yīng)的debug時(shí)需要使用的AndroidMainifest文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baidu.input.module_a" >
<application>
<activity android:name="com.baidu.input.module_a.ModuleAMainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.baidu.input.module_a.ModuleATestActivity"></activity>
</application>
</manifest>
在業(yè)務(wù)module中添加入口Activity類
// 虛擬Activity,用于測試業(yè)務(wù)內(nèi)功能
public class ModuleAMainActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.modulea_activity_main);
}
public void testActivity(View v) {
// ...
}
public void testService(View v) {
// ...
}
}
通過上面的步驟读存,其實(shí)我們已經(jīng)給組件搭了一個(gè)測試的環(huán)境为流,從而讓組件的代碼能夠在單獨(dú)的環(huán)境里運(yùn)行,結(jié)構(gòu)如下圖所示
目前這種做法的缺點(diǎn)在于需要手動(dòng)配置让簿,同時(shí)對(duì)于manifest文件維護(hù)兩份的成本也比較大敬察,后期希望能夠通過插件的方式自動(dòng)進(jìn)行配置,對(duì)于manifest則采用Android Studio支持的多flavor的manifest自動(dòng)合并來去做尔当。
組件的通信
上面說到解耦的時(shí)候提到了莲祸,主項(xiàng)目與各組件之間不允許直接進(jìn)行引用,那么要實(shí)現(xiàn)跨模塊的功能椭迎,就必然涉及到了通信的過程锐帜,這個(gè)過程應(yīng)該如何進(jìn)行呢。
比較通用的方式是使用路由來進(jìn)行這部分的工作畜号。具體后面會(huì)以ARouter的使用為例來進(jìn)行說明缴阎。
集成調(diào)試
在組件的單獨(dú)調(diào)試環(huán)節(jié)我們?cè)黾恿讼旅娴呐渲?/p>
##### 是否單獨(dú)調(diào)試A模塊 #####
DEBUG_MODULE_A=false
##### 主模塊是否需要引入A模塊 #####
NEED_MODULE_A=true
其中NEED_MODULE_A的配置就是用于后期集成調(diào)試準(zhǔn)備的付枫,當(dāng)A模塊的功能完成時(shí)箱蝠,該配置應(yīng)當(dāng)被值為true,便于主模塊對(duì)A模塊進(jìn)行依賴并將A模塊的功能打包到整體APK中晃财。
實(shí)際上替饿,比較合適的做法是语泽,在整個(gè)開發(fā)階段(debug),主模塊中都不應(yīng)當(dāng)包含類似于下面的配置
implementation project(':module-a')
這種依賴方式帶來的缺點(diǎn)是主模塊的開發(fā)人員會(huì)有意無意的直接引用到A模塊中的類视卢,這對(duì)于解耦工作來說是一個(gè)退化過程踱卵,因此可能也需要一個(gè)整體的開關(guān)用于控制開發(fā)階段的依賴問題,比如
if (!DEBUG_MODULE_A.toBoolean() && NEED_MODULE_A.toBoolean() && !DEBUG.toBoolean()) {
implementation project(':module-a')
}
但是正如單獨(dú)調(diào)試中提到的,這種手動(dòng)修改的方式畢竟非常的不友好惋砂,而且對(duì)于我們目前的項(xiàng)目而言不易于操作妒挎,因此考慮使用自定義插件的方式來進(jìn)行,目前對(duì)Gradle插件還不是很熟悉西饵,所以沒有具體去嘗試酝掩,大致的想法應(yīng)該是希望能夠判斷當(dāng)前build的類型并且根據(jù)配置文件的參數(shù)來決定是否
implementation對(duì)應(yīng)的模塊。
組件化規(guī)劃
實(shí)際上組件化是一個(gè)比較長期而且耗時(shí)的過程眷柔,特別是將一個(gè)大工程進(jìn)行組件化期虾,要考慮的內(nèi)容可以說是非常多的,具體體現(xiàn)在下面幾點(diǎn)
路由庫選擇
正如前面所說的驯嘱,目前組件化的工作需要進(jìn)行組件間的通信镶苞,因此必須要有一個(gè)負(fù)責(zé)這部分工作的路由模塊,這個(gè)模塊應(yīng)該如何選擇鞠评,是自行實(shí)現(xiàn)還是選擇第三方等等茂蚓。
調(diào)試環(huán)境
目前調(diào)試環(huán)境的切分方式還不夠自動(dòng)化,可能需要開發(fā)額外的插件
組件化拆分
對(duì)于組件化工作而言剃幌,大部分時(shí)間可能都是消耗在拆分工作上聋涨,現(xiàn)在讓我去想這個(gè)過程我都可以感覺到是很麻煩,但是細(xì)細(xì)考慮這部分的工作负乡,還是有法可循的
- 從產(chǎn)品需求到開發(fā)階段到運(yùn)營階段都有清晰邊界的功能開始拆分
- 拆分過程中依賴項(xiàng)目的模塊繼續(xù)進(jìn)行拆分牍白,比如埋點(diǎn)、網(wǎng)絡(luò)
- 最終將主模塊變成空殼抖棘,僅僅包含一些簡單的拼接邏輯
路由
這里的路由就是根據(jù)路由表將請(qǐng)求發(fā)送到制定的位置淹朋,可以是一個(gè)頁面也可以是一個(gè)服務(wù)抑或是其他形式的內(nèi)容。目前Android平臺(tái)的路由庫還是比較豐富的钉答,那么為什么要有這么一個(gè)路由組件主要有下面幾點(diǎn)原因
開發(fā)與協(xié)作
根據(jù)我們對(duì)路由的定義础芍,Android原生的路由方案一般是通過顯式intent和隱式intent兩種方式實(shí)現(xiàn)的,而在顯式intent的情況下数尿,因?yàn)闀?huì)存在直接的類依賴的問題仑性,導(dǎo)致耦合非常嚴(yán)重;而在隱式intent情況下右蹦,則會(huì)出現(xiàn)規(guī)則集中式管理诊杆,導(dǎo)致協(xié)作變得非常困難。
組件化
組件化是開發(fā)和協(xié)作中作為開發(fā)者所需要面對(duì)的問題何陆,而一旦一款A(yù)PP達(dá)到一定體量的時(shí)候晨汹,業(yè)務(wù)就會(huì)膨脹得比較嚴(yán)重,而開發(fā)團(tuán)隊(duì)的規(guī)模也會(huì)越來越大贷盲,這時(shí)候一般都會(huì)提出組件化的概念淘这。組件化就是將APP按照一定的功能和業(yè)務(wù)拆分成多個(gè)小組件剥扣,不同的組件由不同的開發(fā)小組來負(fù)責(zé),這樣就可以解決大型APP開發(fā)過程中的開發(fā)與協(xié)作的問題铝穷,將這些問題分散到小的APP中钠怯。目前而言組件化已經(jīng)有非常多比較成熟的方案了,而自定義路由框架也可以非常好地解決整個(gè)APP完成組件化之后模塊之間沒有耦合的問題曙聂,因?yàn)闆]有耦合時(shí)使用原生的路由方案肯定是不可以的晦炊。
Native和H5問題
Native與H5的問題主要是由于現(xiàn)在的APP很少是純Native或者純H5的,一般是將兩者進(jìn)行結(jié)合宁脊,那么他們之間需要一個(gè)統(tǒng)一負(fù)責(zé)處理頁面跳轉(zhuǎn)的管理模塊断国,使用路由模塊實(shí)現(xiàn)的中間跳轉(zhuǎn)頁就非常適合處理這種問題。
根據(jù)之前組件化的工作中的描述榆苞,路由是必須使用的一項(xiàng)工具并思,這里我使用ARouter庫來介紹。
ARouter介紹
ARouter是阿里開源的一個(gè)Android平臺(tái)中對(duì)頁面及服務(wù)提供路由功能的中間件语稠,
他有如下特點(diǎn)
- 支持直接解析標(biāo)準(zhǔn)URL進(jìn)行跳轉(zhuǎn),并自動(dòng)注入?yún)?shù)到目標(biāo)頁面中
- 支持多模塊工程使用
- 支持添加多個(gè)攔截器弄砍,自定義攔截順序
- 支持依賴注入仙畦,可單獨(dú)作為依賴注入框架使用
- 支持InstantRun
- 支持MultiDex(Google方案)
- 映射關(guān)系按組分類、多級(jí)管理音婶,按需初始化
- 支持用戶指定全局降級(jí)與局部降級(jí)策略
- 頁面慨畸、攔截器、服務(wù)等組件均自動(dòng)注冊(cè)到框架
- 支持多種方式配置轉(zhuǎn)場動(dòng)畫
- 支持獲取Fragment
- 完全支持Kotlin以及混編
簡單的說ARouter的原理就是在編譯階段根據(jù)注解解釋器對(duì)路由注解衣式,攔截器注解以及自動(dòng)裝配注解注解進(jìn)行解釋并生成輔助代碼寸士,待運(yùn)行期與API接口一起提供給宿主APP使用,其中
路由注解——@Route
路由注解生成的路由表碴卧,是核心路由功能弱卡,之所以使用注解來實(shí)現(xiàn)主要考慮的是大型項(xiàng)目中的界面數(shù)量非常多,如果進(jìn)行手動(dòng)注冊(cè)映射關(guān)系會(huì)非常麻煩住册,需要寫很多重復(fù)冗余的代碼婶博,并且需要調(diào)用很多接口,因此ARouter使用了注解的方式進(jìn)行幫我們自動(dòng)注冊(cè)荧飞。
攔截器注解——@Interceptor
攔截器注解用于對(duì)路由過程進(jìn)行攔截凡人,主要考慮的是原生路由能力無法在頁面跳轉(zhuǎn)的過程中添加自定義邏輯,而這一能力有時(shí)候有非常有必要可以避免許多重復(fù)邏輯的實(shí)現(xiàn)叹阔。ARouter中的攔截器也是通過注解的方式自動(dòng)注冊(cè)的挠轴。
自動(dòng)裝配——@Autowired
編譯期對(duì)Autowired注解的字段進(jìn)行掃描并注冊(cè)到映射文件中,如果需要路由的目標(biāo)界面調(diào)用了ARouter.inject(this)耳幢,那么待運(yùn)行時(shí)ARouter會(huì)查找到編譯期為調(diào)用方生成的輔助類進(jìn)行參數(shù)注入岸晦。
ARouter使用
以一個(gè)實(shí)際的例子來描述ARouter的使用,項(xiàng)目希望的簡要結(jié)構(gòu)如下配置
路由跳轉(zhuǎn)各個(gè)模塊都需要使用,因此在ModuleRouter模塊中引入ARouter所需要的庫委煤,上面一個(gè)是api接口堂油,下面一個(gè)是注解解釋器
dependencies {
// 替換成最新版本, 需要注意的是api
// 要與compiler匹配使用,均使用最新版可以保證兼容
// 最新版本參考github的鏈接
api "com.alibaba:arouter-api:${AROUTER_API}"
annotationProcessor "com.alibaba:arouter-compiler:${AROUTER_COMPILER}"
...
}
由于ModuleRouter模塊可能會(huì)使用到一些ARouter的注解碧绞,因此還需要添加下面的配置代碼
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
}
ModuleA府框、ModuleB以及APP模塊都需要依賴ModuleRouter,而且需要使用到注解讥邻,因此配置如下
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
}
// ...
dependencies {
annotationProcessor "com.alibaba:arouter-compiler:${AROUTER_COMPILER}"
implementation project(':modulerouter')
// ...
}
初始化
ARouter初始化工作推薦盡早進(jìn)行迫靖,因此放在Application的onCreate中
if (isDebug()) { // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 開啟調(diào)試模式(如果在InstantRun模式下運(yùn)行兴使,必須開啟調(diào)試模式系宜!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
}
ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
注解
ARouter的路由功能所需要的路由表是在編譯階段根據(jù)注解生成輔助類中包含的发魄,這里路由主要包含了路由界面和路由服務(wù)兩部分盹牧。注解解釋器已經(jīng)在配置階段在相應(yīng)模塊中通過配置添加了,但是想使用路由能力励幼,就需要在特定地方加上注解汰寓。除此之外還有攔截器注解和自動(dòng)裝配注解
路由界面
// 在支持路由的頁面上添加注解(必選)
// 這里的路徑需要注意的是至少需要有兩級(jí),/xx/xx
@Route(path = "/modulea/test")
public class ModuleATestActivity extends Activity {
...
}
這里對(duì)@Route這個(gè)注解做個(gè)簡單的解釋苹粟,path這個(gè)字段里最前面的兩個(gè)『/』中間的部分是路由表中『組』的標(biāo)識(shí)有滑,后面的內(nèi)容是具體表示∏断鳎『組』這個(gè)概念用于ARouter的分組加載的管理毛好,避免一次性加載所有節(jié)點(diǎn)導(dǎo)致路由表瞬間增大】溜酰可以使用group字段進(jìn)行自定義分組肌访,其余字段部分可以參考源碼中的注釋。
@Route(path = "/com/test" , group = "wangchen")
一旦主動(dòng)指定分組之后艇劫,應(yīng)用內(nèi)路由需要使用 ARouter.getInstance().build(path, group) 進(jìn)行跳轉(zhuǎn)场靴,手動(dòng)指定分組,否則無法找到
路由服務(wù)
對(duì)于需要路由的服務(wù)港准,需要實(shí)現(xiàn)IProvider接口
@Route(path = "/modulea/service")
public class ModuleAServiceImpl implements IProvider {
...
}
特殊服務(wù)
這里提兩個(gè)特殊服務(wù)
對(duì)象解析服務(wù)
ARouter中如果要傳遞自定義對(duì)象旨剥,則需要使用該服務(wù),實(shí)現(xiàn)SerializationService浅缸,并且使用@Route注解
@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
@Override
public void init(Context context) {
}
@Override
public <T> T json2Object(String text, Class<T> clazz) {
return JSON.parseObject(text, clazz);
}
@Override
public String object2Json(Object instance) {
return JSON.toJSONString(instance);
}
}
降級(jí)服務(wù)
降級(jí)服務(wù)表示對(duì)路由失敗的情況的處理轨帜,是全局生效的
// 實(shí)現(xiàn)DegradeService接口,并加上一個(gè)Path內(nèi)容任意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
// do something.
}
@Override
public void init(Context context) {
}
}
攔截器
攔截器全局生效衩椒,需要實(shí)現(xiàn)IInterceptor接口蚌父,priority表示攔截器優(yōu)先級(jí)哮兰,優(yōu)先級(jí)高的攔截器優(yōu)先執(zhí)行
@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
...
}
自動(dòng)裝配
自動(dòng)裝配需要在對(duì)應(yīng)成員變量處加上@Autowired注解
// 為每一個(gè)參數(shù)聲明一個(gè)字段,并使用 @Autowired 標(biāo)注
// URL中不能傳遞Parcelable類型數(shù)據(jù)苟弛,通過ARouter api可以傳遞Parcelable對(duì)象
@Route(path = "/test/activity")
public class Test1Activity extends Activity {
@Autowired
public String name;
@Autowired
int age;
@Autowired(name = "girl") // 通過name來映射URL中的不同參數(shù)
boolean boy;
@Autowired
TestObj obj; // 支持解析自定義對(duì)象喝滞,路由表中需要存在SerializationService服務(wù)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ARouter.getInstance().inject(this);
// ARouter會(huì)自動(dòng)對(duì)字段進(jìn)行賦值,無需主動(dòng)獲取
Log.d("param", name + age + boy);
}
}
注意要盡可能早的調(diào)用
ARouter.getInstance().inject()
如果不需要自動(dòng)裝配膏秫,那么可以不調(diào)用ARouter.getInstance().inject()右遭,但是如果希望可以通過URL跳轉(zhuǎn)的方式進(jìn)入該界面,則依然需要保留@Autowired注解缤削,否則ARouter不知道應(yīng)該如何從URL中提取參數(shù)類型放入Intent內(nèi)窘哈。
發(fā)起路由
路由界面和服務(wù)略有不同
路由界面
針對(duì)上面定義的ModuleATestActivity,我們需要路由該界面時(shí)只需要下面的代碼即可
ARouter.getInstance().build("/modulea/test").navigation();
對(duì)于手動(dòng)指定的分組亭敢,則需要這樣
ARouter.getInstance().build("/modulea/test", "module").navigation();
在實(shí)際項(xiàng)目開發(fā)中滚婉,為了協(xié)調(diào)路路徑的問題,考慮將這部分的靜態(tài)代碼下沉至路由模塊帅刀,因此在路由模塊中添加如下代碼
package com.baidu.input.imerouter;
/**
* Created by wangchen on 02/03/18.
*/
public class RouterPath implements IModuleAPath, IModuleBPath {
}
interface IModuleAPath {
String MODULE_A_TEST = "/modulea/test";
String MODULE_A_SERVICE = "/modulea/service";
}
interface IModuleBPath {
String MODULE_B_TEST = "/moduleb/test";
}
因此注解代碼可以修改成
@Route(path = RouterPath.MODULE_A_TEST)
public class ModuleATestActivity extends Activity {
}
@Route(path = RouterPath.MODULE_A_SERVICE)
public class ModuleAServiceImpl implements ModuleAService {
}
發(fā)起路由代碼可以修改成
ARouter.getInstance().build(RouterPath.MODULE_A_TEST).navigation();
在ARouter官方給的最佳實(shí)踐中描述了這一點(diǎn)扣溺,對(duì)于所有界面跳轉(zhuǎn)都建議使用ARouter的方式來進(jìn)行統(tǒng)一管理哈恰,但是對(duì)于模塊內(nèi)的跳轉(zhuǎn)似乎這么寫又有些難受,因此給出如下的代碼建議
@Route(path = RouterPath.MODULE_A_TEST)
public class ModuleATestActivity extends Activity {
public static void launch(Activity c) {
ARouter.getInstance().build(RouterPath.MODULE_A_TEST).navigation(c);
}
}
這樣對(duì)于所有可以直接引用ModuleATestActivity這個(gè)類的地方(本模塊)就可以以一個(gè)非ARouter的方式進(jìn)行界面跳轉(zhuǎn)荠医。
路由服務(wù)
首先要說的是攻冷,相對(duì)于上面的路由服務(wù)注冊(cè)的代碼,實(shí)際上ARouter建議以如下的方式進(jìn)行路由服務(wù)的聲明
定義服務(wù)接口
public interface ModuleAService extends IProvider {
String callModuleAService(String msg);
}
實(shí)現(xiàn)服務(wù)接口
@Route(path = RouterPath.MODULE_A_SERVICE)
public class ModuleAServiceImpl implements ModuleAService {
@Override
public String callModuleAService(String msg) {
Log.i("ModuleA", msg);
return "ModuleA receive " + msg;
}
@Override
public void init(Context context) {
}
}
這樣做的好處是分離了接口和實(shí)現(xiàn)胁黑,對(duì)于后面的模塊解耦有比較大的幫助。
回過頭來看路由服務(wù)的使用颈畸,ARouter中提供了兩種路由服務(wù)的方式——byType和byName嘁信,這跟它的路由表實(shí)現(xiàn)有關(guān),兩種方式都可以在路由表中找到對(duì)應(yīng)的服務(wù)。
byType
ARouter.getInstance().navigation(ModuleAService.class).callModuleAService("msg");
byName
((ModuleAService) ARouter.getInstance().build(RouterPath.MODULE_A_SERVICE).navigation()).callModuleAService("msg");
這兩種方式在ModuleAService接口只有一個(gè)實(shí)現(xiàn)的時(shí)候沒有問題捷雕,但是如果出現(xiàn)多實(shí)現(xiàn)時(shí)會(huì)有問題,由于路由表加載是一個(gè)map壹甥,因此此時(shí)實(shí)際使用的服務(wù)接口實(shí)現(xiàn)是自動(dòng)生成的路由表加載代碼中順序靠后的一個(gè)救巷,這種情況建議使用byName的方式來規(guī)避沖突。
再來看看為什么要進(jìn)行接口和實(shí)現(xiàn)分離句柠,很多時(shí)候我們需要跨模塊進(jìn)行服務(wù)調(diào)用浦译,如果不進(jìn)行分離直接使用實(shí)現(xiàn)類,那么根據(jù)上面兩種方式溯职,在發(fā)起路由的模塊會(huì)產(chǎn)生一個(gè)對(duì)服務(wù)提供模塊的直接依賴精盅,這回對(duì)模塊解耦產(chǎn)生影響。
但是換成接口和實(shí)現(xiàn)分離的形式來做的話谜酒,依然會(huì)有一個(gè)接口類的依賴叹俏,為了避免這種直接依賴問題,我們需要將接口類下沉到基礎(chǔ)模塊中僻族,這里就是ModuleRouter粘驰,注意下面的package
接口位于路由模塊
package com.baidu.input.imerouter;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Created by wangchen on 02/03/18.
*/
public interface ModuleAService extends IProvider {
String callModuleAService(String msg);
}
實(shí)現(xiàn)位于業(yè)務(wù)模塊
package com.baidu.input.module_a;
import android.content.Context;
import android.util.Log;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.baidu.input.imerouter.ModuleAService;
import com.baidu.input.imerouter.RouterPath;
/**
* Created by wangchen on 02/03/18.
*/
@Route(path = RouterPath.MODULE_A_SERVICE)
public class ModuleAServiceImpl implements ModuleAService {
@Override
public String callModuleAService(String msg) {
Log.i("ModuleA", msg);
return "ModuleA receive " + msg;
}
@Override
public void init(Context context) {
}
}
值得注意的是,希望一個(gè)模塊最多對(duì)外提供一個(gè)服務(wù)接口述么,以確保路由模塊的接口數(shù)量不會(huì)膨脹蝌数,同時(shí)該服務(wù)接口必須滿足開閉原則。
特殊服務(wù)
因?yàn)橹坝懻摰腷yType和byName問題度秘,ARouter的實(shí)現(xiàn)對(duì)于特殊服務(wù)都是使用byType的形式來處理的顶伞,因此如果出現(xiàn)多服務(wù)實(shí)現(xiàn)可能會(huì)出現(xiàn)問題。此時(shí)建議全局僅使用一個(gè)自定義對(duì)象加載服務(wù)和全局降級(jí)服務(wù)剑梳,這兩個(gè)服務(wù)可以放在路由模塊唆貌,便于統(tǒng)一處理。
其他
對(duì)于攔截器和自動(dòng)裝配以及其他路由發(fā)起方式的使用阻荒,可以參考官方demo挠锥,這里不做更多介紹了众羡。
ARouter分析
ARouter注解
首先我們知道ARouter的自動(dòng)注冊(cè)的實(shí)現(xiàn)是利用了編譯期自定義注解的處理來完成的侨赡。ARouter定義的注解的部分源碼位于arouter-annotation,具體的實(shí)現(xiàn)這里不做源碼分析了粱侣,只需要知道編譯期ARouter會(huì)通過注解解釋器生成幫助類羊壹,比如這樣
Routes
我們看下routes這個(gè)包下的內(nèi)容,這個(gè)包下的類都是用于生成路由表的
先看Root的內(nèi)容齐婴,可以看出這里做的事情是將路由分組放入map中油猫,這里涉及到兩個(gè)分組——service和test
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("service", ARouter$$Group$$service.class);
routes.put("test", ARouter$$Group$$test.class);
}
}
以其中一個(gè)分組test為例看代碼,這里做的事情是將這個(gè)test分組內(nèi)的所有具體路由項(xiàng)添加到一個(gè)map中柠偶,路由項(xiàng)的具體信息會(huì)包裝成RouteMeta類的對(duì)象
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("pac", 9); put("ch", 5); put("fl", 6); put("obj", 10); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 10); put("map", 10); put("age", 3); put("url", 8); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", null, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}
除此之外Interceptor中是攔截器路由項(xiàng)加載的類情妖,而Provider中是服務(wù)路由項(xiàng)加載的類
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Interceptors$$app implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(7, Test1Interceptor.class);
}
}
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, 10, -2147483648));
providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, TestService.class, "/service/test", "service", null, 50, -2147483648));
}
}
從Provider這個(gè)自動(dòng)生成類我們還可以發(fā)現(xiàn)一個(gè)問題睬关,對(duì)于服務(wù)而言它可以通過Provider自動(dòng)生成類的loadInto方法加載到路由表中,也可以通過具體所屬組所提供的loadInto方法加載到路由表中毡证,這也是ARouter對(duì)服務(wù)能提供byType和byName兩種路由方式的原因
Autowired
對(duì)于剩下的自動(dòng)生成的類电爹,都是以Autowired這個(gè)關(guān)鍵詞結(jié)尾的,表明他們是負(fù)責(zé)自動(dòng)裝配的代碼料睛,以其中一個(gè)為例
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class Test1Activity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
Test1Activity substitute = (Test1Activity)target;
substitute.name = substitute.getIntent().getStringExtra("name");
substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
substitute.height = substitute.getIntent().getIntExtra("height", substitute.height);
substitute.girl = substitute.getIntent().getBooleanExtra("boy", substitute.girl);
substitute.ch = substitute.getIntent().getCharExtra("ch", substitute.ch);
substitute.fl = substitute.getIntent().getFloatExtra("fl", substitute.fl);
substitute.dou = substitute.getIntent().getDoubleExtra("dou", substitute.dou);
substitute.pac = substitute.getIntent().getParcelableExtra("pac");
if (null != serializationService) {
substitute.obj = serializationService.parseObject(substitute.getIntent().getStringExtra("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
if (null != serializationService) {
substitute.objList = serializationService.parseObject(substitute.getIntent().getStringExtra("objList"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestObj>>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'objList' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
if (null != serializationService) {
substitute.map = serializationService.parseObject(substitute.getIntent().getStringExtra("map"), new com.alibaba.android.arouter.facade.model.TypeWrapper<Map<String, List<TestObj>>>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'map' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
substitute.url = substitute.getIntent().getStringExtra("url");
substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
}
}
對(duì)于這部分的代碼丐箩,大部分比較清晰,總體邏輯是從Activity接收到的intent中提取內(nèi)容并賦值給對(duì)應(yīng)的屬性恤煞。需要注意的是如下幾點(diǎn)
- @Autowired修飾的屬性不能為private
- @Autowired如果修飾的是自定義對(duì)象屎勘,那么需要有一個(gè)SerializationService服務(wù)實(shí)現(xiàn)
- @Autowired可以用來修飾服務(wù),自動(dòng)裝配的時(shí)候會(huì)找到對(duì)應(yīng)的服務(wù)實(shí)現(xiàn)賦值
總結(jié)
綜合上面的內(nèi)容居扒,我們可以得出下面的結(jié)論
- ARouter 的自動(dòng)注冊(cè)機(jī)制一定是通過這些路由清單類來實(shí)現(xiàn)的
- 我們可以通過兩種方式來找到定義的 PROVIDER 類型的路由節(jié)點(diǎn)
- 自動(dòng)賦值功能的實(shí)現(xiàn)概漱,一定是在頁面被路由打開時(shí)調(diào)用了生成的幫助類(ISyringe接口的 inject(Object target) 方法)
初始化
LogisticsCenter.init
ARouter的初始化是通過ARouter.init方法來實(shí)現(xiàn)的,這個(gè)方法最終是通過LogisticsCenter.init來實(shí)現(xiàn)具體的邏輯的
/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generate by arouter-compiler.
// 掃描對(duì)應(yīng)包名下(實(shí)際上就是routes包)的所有類
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// 保存當(dāng)前記錄的版本信息
PackageUtils.updateVersion(context); // Save new version name when router map update finish.
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
// 對(duì)所有包下的類苔货,分情況進(jìn)行加載犀概,加載到Warehouse的不同的屬性中
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
根據(jù)這部分的邏輯,我們可以得出下面的結(jié)論
- loadInto這個(gè)方法夜惭,其實(shí)就是調(diào)用了剛剛我們分析的自動(dòng)生成的那些類的loadInto方法姻灶,
- 初始化之后的路由表是保存在Warehouse這個(gè)類的一些成員變量里的。
- 初始化只加載了Root诈茧,Provider产喉,Interceptor三個(gè)類的路由項(xiàng)
實(shí)際上對(duì)于第三點(diǎn),正是之前說的敢会,ARouter是分組懶加載的曾沈,所以初始化的時(shí)候并未做完全路由加載。
Warehouse
然后可以看看Warehouse里的內(nèi)容
/**
* Storage of route meta and other data.
*
* @author zhilong <a href="mailto:zhilong.lzl@alibaba-inc.com">Contact me.</a>
* @version 1.0
* @since 2017/2/23 下午1:39
*/
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
這里有6個(gè)成員變量鸥昏,有三個(gè)是在剛剛的init過程中初始化的——groupsIndex塞俱、providersIndex、interceptorsIndex吏垮,另外三個(gè)分別描述如下
routes——這個(gè)用于保存完整的路由表障涯,是在路由表更新的方法中不斷更新的,懶加載過程對(duì)應(yīng)于LogisticsCenter.completion方法
providers——這個(gè)用于緩存服務(wù)實(shí)現(xiàn)具體的類的對(duì)象膳汪,避免重復(fù)創(chuàng)建服務(wù)實(shí)現(xiàn)類的對(duì)象唯蝶,懶加載過程對(duì)應(yīng)于LogisticsCenter.completion方法
interceptors——這個(gè)用于保存攔截器的優(yōu)先級(jí)順序,因?yàn)槲覀償r截器是按照優(yōu)先級(jí)先后來處理的遗嗽,因此必然需要一個(gè)列表來保存這個(gè)優(yōu)先級(jí)粘我,懶加載過程對(duì)應(yīng)于InterceptorServiceImpl.init方法
LogisticsCenter.completion
剛剛提到了LogisticsCenter.completion這個(gè)方法,ARouter的初始化過程嚴(yán)格來說應(yīng)該也包含了這個(gè)『完善路由』的方法
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should completion by this method.
*/
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // Reload
}
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must be implememt IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
這里的PostCard保存的是路由過程需要的一些全部信息痹换,從上面的代碼里我們也可以看出來下面幾點(diǎn)
- 對(duì)于不在Warehouse.routes路由表中的路由項(xiàng)征字,需要加載并放在Warehouse.routes中
- 對(duì)于已經(jīng)在路由表中的項(xiàng)都弹,會(huì)將相應(yīng)信息放入PostCard中
- 如果路由項(xiàng)是Provider(服務(wù))且不存在于服務(wù)路由表緩存中時(shí),會(huì)實(shí)例化服務(wù)并放入緩存
InterceptorServiceImpl.init
InterceptorServiceImpl實(shí)際上也是一個(gè)服務(wù)的具體實(shí)現(xiàn)匙姜,用于管理所有的攔截器的初始化缔杉,它在ARouter初始化(LogisticsCenter.init)之后執(zhí)行,具體看下InterceptorServiceImpl.init的實(shí)現(xiàn)代碼
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
可以看到攔截器是異步加載的搁料,而且是從interceptorsIndex中提取所有的一次性完全加載到Warehouse管理的內(nèi)存隊(duì)列中的或详。
綜合上面所有我們可以大概知道ARouter的路由表初始化的整個(gè)過程。
發(fā)起路由
這里只分析byType和byName兩種發(fā)起路由的方式郭计,這兩種方式最終會(huì)執(zhí)行到_ARouter類內(nèi)的兩個(gè)方法
// byType
protected <T> T navigation(Class<? extends T> service) {
// .............
}
// byName
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, NavigationCallback callback) {
// .............
}
byType
byType內(nèi)部實(shí)現(xiàn)如下
protected <T> T navigation(Class<? extends T> service) {
try {
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
// Compatible 1.0.5 compiler sdk.
if (null == postcard) { // No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
第一個(gè)方法用于構(gòu)造路由信息PostCard對(duì)象霸琴,可以看到是從路由表中根據(jù)服務(wù)接口名找到路由基本信息
/**
* Build postcard by serviceName
*
* @param serviceName interfaceName
* @return postcard
*/
public static Postcard buildProvider(String serviceName) {
RouteMeta meta = Warehouse.providersIndex.get(serviceName);
if (null == meta) {
return null;
} else {
return new Postcard(meta.getPath(), meta.getGroup());
}
}
然后使用上面分析過的completion方法完善PostCard對(duì)象信息,因?yàn)閎yType只用于路由服務(wù)昭伸,因此最后將Provider實(shí)例對(duì)象返回
byName
byName內(nèi)部實(shí)現(xiàn)如下
/**
* Use router navigation.
*
* @param context Activity or null.
* @param postcard Route metas
* @param requestCode RequestCode
* @param callback cb
*/
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) { // Show friendly tips for user.
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
if (null != callback) {
callback.onLost(postcard);
} else { // No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
byName的方式梧乘,方法本身包含PostCard對(duì)象參數(shù),該參數(shù)是根據(jù)路由項(xiàng)的Group和Path構(gòu)造的庐杨,從上面的代碼里看到主要做了幾件事
- 調(diào)用completion方法完善路由表和postcard對(duì)象信息
- 回調(diào)告知路由狀態(tài)
- 攔截器工作
- 執(zhí)行路由跳轉(zhuǎn)
我們看下最后執(zhí)行路由跳轉(zhuǎn)的方法_navigation(context, postcard, requestCode, callback);
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Navigation in main looper.
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (requestCode > 0) { // Need start for result
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
}
if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
}
if (null != callback) { // Navigation over.
callback.onArrival(postcard);
}
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
上面的代碼主要也是分路由類別做了不同的事情
- Activity——?jiǎng)?chuàng)建intent选调,塞入flag,extras等內(nèi)容并執(zhí)行界面跳轉(zhuǎn)
- Provider——返回Provider實(shí)現(xiàn)對(duì)象灵份,供byName調(diào)用者調(diào)用服務(wù)接口
- Fragment——返回Fragment實(shí)例
以上就是對(duì)ARouter實(shí)現(xiàn)的一個(gè)簡單的分析仁堪,如果有興趣的話可以參考源碼閱讀更多的內(nèi)容