簡介
Android平臺中對頁面将硝、服務(wù)提供路由功能的中間件蛉腌,我的目標(biāo)是 —— 簡單且夠用恰响。具體的使用可以參考:https://github.com/alibaba/ARouter拓颓。如果對內(nèi)部的詳細(xì)原理感興趣可以參考:http://www.reibang.com/p/3c4f4e3e621f讲坎。下文分析的代碼來源于ARouter官方Demo借跪,閱讀下文之前建議先下載一份源碼運(yùn)行并對照源碼進(jìn)行比對政己,因為下文中我會用官方demo的源碼截圖。
APT
APT技術(shù)可以讓我們通過自定義注解動態(tài)生成編譯后的class代碼掏愁,具體的使用我在這里就不詳細(xì)說了歇由,感興趣的可以參考我以前寫的:編寫最基本的APT Demo。
我這里直接來看下ARouter說涉及到的幾個注解果港,以及編譯之后動態(tài)生成的代碼沦泌。
annotation
其中Param已被Autowired代替
Route:用于標(biāo)記我們需要跳轉(zhuǎn)的四大組件(可以通過Intent跳轉(zhuǎn)的,因為其實ARouter 內(nèi)部最后也是通過Intent來進(jìn)行跳轉(zhuǎn))辛掠、service(此處的sevice是類似于后臺的服務(wù)谢谦,需要繼承IProvider)。
Interceptor:主要的作用是通過AOP技術(shù)(面向切面編程)在我們進(jìn)行頁面跳轉(zhuǎn)之前可以進(jìn)行一系列的攔截操作
Autowired:主要的作用是通過IOC技術(shù)(依賴注入)獲取頁面跳轉(zhuǎn)的參數(shù)萝衩。
注解解析
可以看到對應(yīng)了上面的三個注解回挽。這里具體的代碼我就不分析了,感興趣的可以直接去看源碼(雖然不算很難但是比較繁瑣猩谊,一定要耐心)千劈,當(dāng)我們?nèi)志幾g以后會動態(tài)生成以下代碼
ARouter$$Root$$app:因為Arouter采取的是懶加載技術(shù),所以我們需要對router進(jìn)行分組牌捷,這里的Root內(nèi)部就是通過Map以組名為key存儲了每組router的信息信息墙牌。
ARouter$$Group$$xxx:我們按不同的router類型進(jìn)行分組袁梗,內(nèi)部通過Map以router path存儲了具體router的信息
ARouter$$Interceptors$$app:其中app是我們的module名(通過查看源碼可知),內(nèi)部
以priority優(yōu)先級為key存儲了具體的Interceptors的class信息憔古。
ARouter$$Providers$$app:其中app是我們的module名(通過查看源碼可知)遮怜,內(nèi)部以類的完整限定名為key保存了service的信息,結(jié)構(gòu)同ARouter$$Group$$xxx一致鸿市,只是用于不同的功能锯梁。
關(guān)于ARouter的APT分支就到這了,下面來看下ARouter的初始化焰情。
init
這正式分析初始化之前我們先了解幾個類
ARouter:_ARouter的代理類陌凳,這里采用了代理模式,其實ARouter對外只開放了這一個api内舟,所有的操作基本上都是通過ARouter來完成了合敦。
_ARouter:ARouter所代理得到類,功能的具體執(zhí)行者验游。
LogisticsCenter:物流調(diào)度中心充岛,對路由信息進(jìn)行解析和加工。
Warehouse:倉庫耕蝉,存儲了所有具體的router崔梗、interceptors、service等信息垒在,內(nèi)部是一系列的Map蒜魄。
ARouter的初始化流程:ARouter#init ──》_ARouter#init ──》LogisticsCenter#init ──》Warehouse#Map#put
// 獲得指定包名下的所有類名
List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
for (String className : classFileNames) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root. 通過反射找到Arouter$$Root$$xx 加載根路由集合
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta 通過反射找到Arouter$$Interceptors$$xx 加載攔截器集合
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex 通過反射找到Arouter$$Providers$$xx 加載服務(wù)集合 此處的service對應(yīng)后臺的概念 不是四大組件的service
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
其中第三步和第四步的代碼會根據(jù)指定報名查找下面所有的類信息,然后根據(jù)類得到全限定名進(jìn)行功能分組场躯,并把信息保存在Warehouse的Map中谈为。到此Arouter的初始化過程就完成。
Router的跳轉(zhuǎn)和參數(shù)注入
ARouter.getInstance().build("/test/activity1")
.withString("name", "老王")
.withInt("age", 18)
.withBoolean("boy", true)
.withLong("high", 180)
.withString("url", "https://a.b.c")
.withParcelable("pac", testParcelable)
.withObject("obj", testObj)
.navigation();
public class Test1Activity extends AppCompatActivity {
@Autowired
String name;
@Autowired
int age;
@Autowired(name = "boy")
boolean girl;
@Autowired
TestParcelable pac;
@Autowired
TestObj obj;
private long high;
@Autowired
String url;
@Autowired
HelloService helloService;
}
其中Test1Activity 通過APT動態(tài)生成的代碼如下:
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("pac", 9); put("obj", 10); put("name", 8); put("boy", 0); put("age", 3); put("url", 8); }}, -1, -2147483648));
所有的跳轉(zhuǎn)參數(shù)都保存在map中踢关,其中key是一一對應(yīng)伞鲫,而value是參數(shù)類型,對題對照如下:
public enum TypeKind {
// Base type
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
// Other type
STRING,
PARCELABLE,
OBJECT;
}
下面我們來看具體的跳轉(zhuǎn)流程耘成。
build
ARouter.getInstance().build("/test/activity1")升溫我們說過ARouter是_ARouter的代理的類榔昔,所有的api最終都會進(jìn)入到真正的執(zhí)行類_ARouter驹闰。
_ARouter#build
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
//查找是否存在重定向服務(wù)
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
}
這里我們先來看下PathReplaceService這個類
public interface PathReplaceService extends IProvider {
/**
* For normal path.
*
* @param path raw path
*/
String forString(String path);
/**
* For uri type.
*
* @param uri raw uri
*/
Uri forUri(Uri uri);
}
PathReplaceService我稱它為重定向服務(wù)(具體怎么使用請參考官網(wǎng)文檔)瘪菌,它繼承IProvider,那IProvider是什么嘹朗,其實他是用來實現(xiàn)service的师妙,所以PathReplaceService就相當(dāng)于我們自己的自定義服務(wù),唯一的區(qū)別是屹培,自定義服務(wù)需要我們顯示去調(diào)用默穴,跟調(diào)用router一樣怔檩,但是PathReplaceService不需要顯示調(diào)用,他是作用于所有服務(wù)和路由的蓄诽,而且不管你實現(xiàn)了幾個PathReplaceService薛训,最終全局都只會保存在APT時掃描到的最后一個服務(wù)。為什么這么說仑氛,請看下面的代碼:
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
//第一個重定向服務(wù)
providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/redirect/r1", "redirect", null, -1, -2147483648));
//第二個重定向服務(wù)
providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl2.class, "/redirect/r2", "redirect", null, -1, -2147483648));
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, -1, -2147483648));
}
}
因為第一個和第二個重定向服務(wù)的key是一樣都是PathReplaceService的類全限定名乙埃,所以第一個服務(wù)會被覆蓋掉。好了關(guān)于PathReplaceService我們就說到這锯岖,他是我們一個重定向服務(wù)介袜,作用域所有的跳轉(zhuǎn),而且全局只有一個出吹。
我們繼續(xù)往下分析遇伞,如果單例沒有實現(xiàn)自定義重定向服務(wù)的時候,PathReplaceService pService == null捶牢,所以會直接調(diào)用兩個參數(shù)的重載的build方法鸠珠。
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
這里有調(diào)用了一遍ARouter.getInstance().navigation(PathReplaceService.class),感覺沒必要啊秋麸,但是不管肯定還是一樣的返回空跳芳。所以最終ARouter.getInstance().build()會返回一個Postcard(個包含跳轉(zhuǎn)信息的容器,包含跳轉(zhuǎn)參數(shù)和目標(biāo)類的信息)竹勉。下面進(jìn)入真正的跳轉(zhuǎn)飞盆。其實真正的跳轉(zhuǎn)位于_ARouter#navigation。
_ARouter#navigation
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
//完善跳轉(zhuǎn)信息
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
……………………
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
//不是綠色通道所以會進(jìn)入默認(rèn)interceptorService.doInterceptions
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;
}
InterceptorService是我們在初始化完成以后ARouter為我們自動注冊的攔截器服務(wù)次乓,因為我們并沒有為我們得到路由匹配相應(yīng)的攔截器吓歇,所以應(yīng)該會進(jìn)入onContinue方法,經(jīng)過斷點(diǎn)調(diào)試確實和我們想的一樣票腰,可是onContinue是個回調(diào)函數(shù)城看,它又具體是在哪被調(diào)用的呢?我們經(jīng)過查找發(fā)現(xiàn)是在InterceptorServiceImpl中
InterceptorServiceImpl#doInterceptions
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_excute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else {
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
這里開了一個線程用來執(zhí)行攔截器或者普通的跳轉(zhuǎn)所以調(diào)用了callback.onContinue杏慰,接下來就進(jìn)入到我們真正的跳轉(zhuǎn)執(zhí)行的地方了测柠。
_ARouter#_navigation
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);
}
// 因為上文我們是在子線程中檢查是否有匹配的攔截器,所以我們要在這里切換到UI線程執(zhí)行具體的跳轉(zhuǎn)
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;
}
這里的代碼比較簡單缘滥,就是調(diào)用了Android原生的Intent進(jìn)行跳轉(zhuǎn)轰胁,然后根據(jù)不同的狀態(tài),調(diào)用一些回調(diào)函數(shù)朝扼。到此關(guān)于ARouter的跳轉(zhuǎn)到這里就結(jié)束了赃阀,下面我們來看下目標(biāo)對象的參數(shù)是如何獲取的。
Autowired
這里在分析參數(shù)獲取之前我們先廢話2句擎颖,在看到Autowired注解的時候榛斯,是不是感覺似曾相識观游,沒錯這里的原理跟ButterKnife是一毛一樣的,我強(qiáng)烈懷疑Arouter作者是參考ButterKnife代碼寫的驮俗,所以當(dāng)我們分析完Autowired的時候懂缕,其實就相當(dāng)于把ButterKnife也給分析了,哈哈王凑,正式一舉兩得啊提佣。還有,這種開發(fā)思想其實在后臺開發(fā)中非常普遍荤崇,比如大名鼎鼎的Spring就是這種IOC(控制反轉(zhuǎn))思想的最佳代表拌屏。好了,下面進(jìn)入正題术荤。
Autowired注解處理器
當(dāng)我們在編譯過程中倚喂,系統(tǒng)會是掃描有Autowired注解的成員變量類,然后生成自動生成以下代碼:
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", 0);
substitute.girl = substitute.getIntent().getBooleanExtra("boy", false);
substitute.pac = substitute.getIntent().getParcelableExtra("pac");
if (null != serializationService) {
substitute.obj = serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class);
} else {
Log.e("ARouter::", "You want automatic inject the field 'obj' 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);
}
}
這里的代碼很簡單瓣戚,應(yīng)該能直接看懂端圈,我們先來看他的父類ISyringe,他其實相當(dāng)于一個模板類子库,為了便于編程ARouter內(nèi)核提供了許多的模板類舱权,存儲在如下路徑中:
那么為什么要提供模板類呢?簡單了來說仑嗅,當(dāng)我們在變成過程中宴倍,由于框架作者并不知道哪些具體類被標(biāo)注了注解,所以要動態(tài)獲取對象仓技,只能通過反射動態(tài)來獲取實例鸵贬,然后調(diào)用接口的方法來執(zhí)行具體的操作,這就是多態(tài)的概念脖捻,如下代碼所示:
這里插一句阔逼,反射是會影響性能的,所以一般我們在編程中除非萬不得已地沮,否則盡量不要采用反射嗜浮,但是這里是activity初始化的時候反射,本來就會進(jìn)行大量耗時的操作摩疑,哪怕有一點(diǎn)點(diǎn)的性能損耗也是可以接受的危融。還記得Arouter的初始化嗎?官網(wǎng)上有一句原話是這么說的:
大家有沒有想過未荒,為什么要盡可能早的初始化专挪,我想除了要掃描大量的對象并保存到全局的map集合中以外,跟初始化的時候用到反射也有關(guān)系吧片排,畢竟還是有性能損耗的寨腔。如下所示
總結(jié)
好了,到這我們已經(jīng)把頁面跳轉(zhuǎn)和參數(shù)綁定都分析完了率寡,剩下的重定向迫卢,攔截器,降級等很多其他功能冶共,其實都是在跳轉(zhuǎn)的過程中插入的一些攔截操作而已乾蛤,我相信只要大家只要耐下心來看代碼都是可以看明白的。
請參考ARouter 源碼淺析第二篇