ARouter是阿里2017年開源的頁面路由框架,是今年比較火的一個(gè)開源框架典鸡,目前在Github上已經(jīng)有2.2k的小星星了。官方對這個(gè)框架的定義是ARouter是阿里巴巴開源的Android平臺中對頁面、服務(wù)提供路由功能的中間件净当,提倡的是簡單且夠用翎迁。為了跟上潮流栋猖,我也打算來學(xué)習(xí)下這個(gè)開源框架,整個(gè)預(yù)計(jì)會(huì)分成四五個(gè)系列汪榔。今天我們先來看下基本使用和頁面注冊的源碼掂铐。
Google提供的原聲路由主要是通過intent,可以分成顯示和隱式兩種揍异。顯示的方案會(huì)導(dǎo)致類之間的直接依賴問題全陨,耦合嚴(yán)重;隱式intent需要的配置清單中統(tǒng)一聲明衷掷,首先有個(gè)暴露的問題辱姨,另外在多模塊開發(fā)中協(xié)作也比較困難。只要調(diào)用startActivity后面的環(huán)節(jié)我們就無法控制了戚嗅,在出現(xiàn)錯(cuò)誤時(shí)無能為力雨涛,而ARouter可以在跳轉(zhuǎn)過程中進(jìn)行攔截,出現(xiàn)錯(cuò)誤時(shí)可以實(shí)現(xiàn)降級策略懦胞,這個(gè)我們今天就不涉及到替久,后面我們會(huì)專門講。今天的分享會(huì)從下面幾個(gè)方面躏尉。
- ARouter配置
- activity頁面之間跳轉(zhuǎn)
- APT技術(shù)
- SPI技術(shù)
- 頁面注冊源碼解析
我們就不直接擼demo了蚯根,直接使用官方的demo進(jìn)行分享。
1.ARouter配置
配置之前我們先看下官方宣稱的ARouter的優(yōu)勢:
- 直接解析URL路由胀糜,解析參數(shù)并賦值
- 支持多模塊項(xiàng)目
- 支持InstantRun
- 允許自定義攔截器
- ARouter可以提供IoC容器
- 映射關(guān)系自動(dòng)注冊
- 靈活的降級策略
簡單講颅拦,就是很牛X,幾乎包含頁面跳轉(zhuǎn)的所需要的所有功能教藻。
在使用之前需要先配置gradle距帅,目前api最新的版本是1.2.1.1,處理器的最新版本是1.1.2.1.
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
}
dependencies {
// 替換成最新版本, 需要注意的是api
// 要與compiler匹配使用括堤,均使用最新版可以保證兼容
compile 'com.alibaba:arouter-api:1.2.1.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.2.1'
...
}
接著需要盡早的初始化ARouter碌秸,可以考慮在Application中進(jìn)行初始化:
ARouter.init(mApplication); // 盡可能早绍移,推薦在Application中初始化
初始化工作就是這樣,接著我們看下頁面跳轉(zhuǎn)怎么玩讥电。
2.頁面跳轉(zhuǎn)使用
首先需要在支持路由的頁面上添加注解登夫,路徑path至少需要有兩級,比如我們需要跳轉(zhuǎn)到Test2Activity
,那么需要在activity上面配置path,具體內(nèi)容不一定是/test/activity2允趟,可以自由發(fā)揮恼策。
@Route(path = "/test/activity2")
public class Test2Activity extends AppCompatActivity
然后直接像下面這樣就行了,build中的參數(shù)就是上面配置的路徑潮剪。
ARouter.getInstance().build("/test/activity2").navigation();
很簡單有木有涣楷?
如果需要跳轉(zhuǎn)的時(shí)候攜帶參數(shù)呢?
ARouter.getInstance().build("/test/activity2").withString("key1", "value1")
.navigation();
是不是so easy?ARouter提供了很多的withXX方法抗碰,可以攜帶基本類型,Object,Parcelable等等狮斗,里面的原理也是通過Bundle進(jìn)行攜帶mBundle.putXX
如果希望實(shí)現(xiàn)類似startActivityForResult呢?只需要在navigation中傳入兩個(gè)參數(shù)弧蝇,第一個(gè)就是Context碳褒,第二個(gè)參數(shù)是requestCode。
ARouter.getInstance().build("/test/activity2")
.withString("key1", "value1")
.navigation(this, 999);
在Test2Activity中就可以傳遞結(jié)果:
setResult(int resultCode)
或者
setResult(int resultCode, Intent data)
這里為了偷懶我們只是setResult(100);
然后我們就可以在onActivityResult中得到數(shù)據(jù),根據(jù)requestCode可以拿到數(shù)據(jù)看疗,我們這里只是彈個(gè)Toast沙峻。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 999:
Toast.makeText(this, "get resultCode:" + String.valueOf(resultCode), Toast.LENGTH_LONG).show();
default:
break;
}
}
基本跳轉(zhuǎn)就是上面幾種情況,接下來我們來看看源碼是怎么實(shí)現(xiàn)的两芳。
3.APT技術(shù)
APT摔寨,就是Annotation Processing Tool 的簡稱,就是可以在代碼編譯期間對注解進(jìn)行處理怖辆,并且生成Java文件是复,減少手動(dòng)的代碼輸入。這里我們就不多做介紹竖螃,這個(gè)不太清楚的可以參考我之前的分享Android模塊開發(fā)之APT技術(shù)淑廊。
4.SPI技術(shù)
Java提供的SPI全名就是Service Provider Interface,其實(shí)就是為某個(gè)接口尋找服務(wù)的機(jī)制,有點(diǎn)類似IOC的思想特咆,將裝配的控制權(quán)移交給ServiceLoader季惩。SPI在平時(shí)我們用到的會(huì)比較少,但是在Android模塊開發(fā)中就會(huì)比較有用坚弱,不同的模塊可以基于接口編程蜀备,每個(gè)模塊有不同的實(shí)現(xiàn)service provider,然后通過SPI機(jī)制自動(dòng)注冊到一個(gè)配置文件中关摇,就可以實(shí)現(xiàn)在程序運(yùn)行時(shí)掃描加載同一接口的不同service provider荒叶。不太清楚的可以參考之前的分析Android模塊開發(fā)之SPI。
5.頁面注冊源碼分析
分析源碼之前我們需要先了解下框架的整個(gè)架構(gòu)输虱,這里直接引用官方的一張圖片些楣。
- 最基礎(chǔ)的就是Compiler這個(gè)SDK,主要是用來在編譯期間處理注解Router/Interceptor/Autowire三個(gè)注解,在編譯期間自動(dòng)注冊注解標(biāo)注的類愁茁,成員變量等蚕钦。
- API的SDK是用戶在運(yùn)行期使用。Launcher這一層只有ARouter和_ ARouter, ARouter就是我們需要打交道的接口鹅很,這里使用了代理模式嘶居,實(shí)際ARouter是通過_ ARouter進(jìn)行工作。
- 第二層也是綠色的促煮,用戶也是可以調(diào)用到邮屁,這里主要是提供接口,我們可以擴(kuò)展服務(wù)菠齿,比如攔截器服務(wù)佑吝,成員變量裝配服務(wù)等,Template主要是提供模版接口绳匀,這個(gè)SDK會(huì)在編譯期生成一些映射文件芋忿,而這些映射文件會(huì)按照Template組件中提供的模板來生成,這樣按照一定的規(guī)則和約束生成映射文件也方便Route在運(yùn)行的時(shí)候進(jìn)行讀取疾棵。
- Ware House類似一個(gè)倉庫戈钢,主要存儲ARouter在運(yùn)行期間加載的一些配置文件以及映射關(guān)系。
- Thread則是提供了線程池是尔,因?yàn)榇嬖诙鄠€(gè)攔截器的時(shí)候以及跳轉(zhuǎn)過程中都是需要異步執(zhí)行的逆趣。
- 再下一層就是Logistics Center,從名字上翻譯就是物流中心嗜历,整個(gè)SDK的流轉(zhuǎn)以及內(nèi)部調(diào)用最終都會(huì)下沉到這一層宣渗。
我們先看下三個(gè)主要的注解,這個(gè)我刪掉了很多梨州,有需要的可以自行參考源碼痕囱。可以看出來三個(gè)注解的有效期間都是編譯期間暴匠,關(guān)于注解不太清楚的可以參考我們之前的分享反射注解與動(dòng)態(tài)代理綜合使用
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
/**
* Path of route
*/
String path();
……
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// Mark param's name or service name.
String name() default "";
// If required, app will be crash when value is null.
// Primitive type wont be check!
boolean required() default false;
// Description of the field
String desc() default "No desc.";
}
其實(shí)小伙伴們也可以猜出來鞍恢,ARouter會(huì)在編譯的時(shí)候?qū)outer注解標(biāo)注的activity掃描出來,并且按照定義好的模版生成裝配關(guān)系的代碼每窖。這個(gè)其實(shí)就是APT技術(shù)帮掉,我先前已經(jīng)做好鋪墊,英明神武吧:)窒典。簡單說就是要自定義注解的處理器蟆炊,繼承自AbstractProcessor,需要自己實(shí)現(xiàn)process方法來處理注解瀑志,然后需要用到SPI技術(shù)來注冊我們自己定義的注解處理器涩搓,之后JVM就能在編譯期間通過serviceLoader來找到我們的注解處理器balabala污秆。這里篇幅限制我們就不展開反復(fù)說了,不清楚的同學(xué)自行參考上面的鏈接昧甘。
ARouter的process方法比較長良拼,我們就看下生成的文件放在哪里,包名就是public static final String PACKAGE_OF_GENERATE_FILE = "com.alibaba.android.arouter.routes";
那么文件名呢充边?還記得我們前面跳轉(zhuǎn)Test2Activity的path是什么嗎庸推?@Route(path = "/test/activity2")
其中test就是group,所以我們文件名就是groupFileName = ARouter$$Group$$test
public static final String SEPARATOR = "$$";
public static final String PROJECT = "ARouter";
public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + "Group" + SEPARATOR;
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
}
那么我們來看看ARouter自動(dòng)生成的activity裝配關(guān)系是怎么樣的浇冰。
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
}
}
有一個(gè)需要注意的地方就是put("key1", 8)
予弧,還記得我們上面參數(shù)跳轉(zhuǎn)應(yīng)該是withString("key1", "value1")
, 為什么這里是8?其實(shí)在編譯的時(shí)候做了處理湖饱,8代表類型String掖蛤。
public enum TypeKind {
// Base type
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
// Other type
STRING,
PARCELABLE,
OBJECT;
}
頁面自動(dòng)注冊的源碼基本就是上面這些內(nèi)容,我們最后引用一張官方的圖片井厌。
首先通過注解處理器掃出被標(biāo)注的類文件蚓庭;然后按照不同種類的源文件進(jìn)行分類,不僅僅提供了跳轉(zhuǎn)功能仅仆,它也能夠?qū)崿F(xiàn)模塊之間的解耦器赞,其實(shí)ARouter中的所有組件都是自動(dòng)注冊的
在按照不同種類的源文件進(jìn)行分類完成之后,就能夠按照固定的命名格式生成映射文件墓拜,這部分完成之后就意味著編譯期的部分已經(jīng)結(jié)束了港柜;
而最后一步的初始化其實(shí)是發(fā)生在運(yùn)行期的,在運(yùn)行期只需要通過固定的包名來加載映射文件就可以了咳榜,這就是頁面自動(dòng)注冊的整個(gè)流程夏醉。
6.總結(jié)
到這里相信小伙伴們對于ARouter頁面跳轉(zhuǎn)的基本使用已經(jīng)很熟悉了。我們也簡單分析了下頁面注冊的原理涌韩,ARouter可以通過注解自動(dòng)注冊并且在編譯期間生成映射關(guān)系畔柔,在運(yùn)行的時(shí)候就可以加載文件,通過path就可以順利跳轉(zhuǎn)到目標(biāo)頁面臣樱。
好了靶擦,今天車就開到這了。估計(jì)再說下去小伙伴們該睡覺了雇毫,關(guān)于加載配置文件和頁面跳轉(zhuǎn)我們就在解析二中分析玄捕,歡迎關(guān)注。
你們的贊是我最大的動(dòng)力棚放,謝謝枚粘!
關(guān)于注解和APT技術(shù)與SPI技術(shù)不太清楚的小伙伴可自行參考下面鏈接:
Android模塊開發(fā)之APT技術(shù)
Android模塊開發(fā)之SPI
歡迎關(guān)注公眾號:JueCode