組件化被越來(lái)越多的Android項(xiàng)目采用,而作為組件化的基礎(chǔ)——路由也是重中之重夫偶。本篇文章將詳細(xì)的分析阿里巴巴開源的路由框架ARouter厕宗。從源碼的角度解釋為什么這樣使用,以及避免做什么奠货,讓你使用地更加順滑介褥。
項(xiàng)目地址
項(xiàng)目結(jié)構(gòu)
我們把項(xiàng)目clone到本地,打開ARouter項(xiàng)目递惋,可以看到分為如下幾個(gè)Module:
其中app柔滔、module-java、module-kotlin是演示demo相關(guān)的萍虽,也就是使用的示例睛廊。
arouter-register是1.3.0版本新添加的gradle插件的代碼,用于路由表的自動(dòng)注冊(cè)贩挣,可以縮短初始化時(shí)間喉前,解決應(yīng)用加固導(dǎo)致無(wú)法直接訪問dex文件初始化失敗的問題。(本篇文章不涉及這個(gè)模塊)
arouter-annotation包含了注解類以及攜帶數(shù)據(jù)的bean王财;
arouter-compiler包含了注解處理類卵迂,通過java的Annotation Processor Tool按照定義的Processor生成所需要的類。
以demo為例子绒净,當(dāng)運(yùn)行項(xiàng)目后會(huì)在build文件下生成arouter相關(guān)的代碼:
我們先不用去關(guān)心生成代碼的細(xì)節(jié)见咒,只需要知道按照用法的提示添加諸如Route等注解之后,arouter-compiler中的注解處理類會(huì)自動(dòng)幫我們生成需要的代碼(如上圖所示)挂疆。對(duì)源碼的分析也只需要知道生成的類的做了什么就夠了改览。
本篇文章也不討論生成代碼細(xì)節(jié),如果想了解apt的生成過程缤言,可以參考:Java注解處理器 以及更方便生成的Java代碼的庫(kù)javapoet宝当。
arouter-api提供了給我們使用的api,以實(shí)現(xiàn)路由功能胆萧。
那我們就從api開始分析庆揩。
源碼分析
init
按照官方說(shuō)明,我們找到Arouter的入口跌穗,也就是初始化的地方:
if (isDebug()) { // 這兩行必須寫在init之前订晌,否則這些配置在init過程中將無(wú)效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 開啟調(diào)試模式(如果在InstantRun模式下運(yùn)行,必須開啟調(diào)試模式蚌吸!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
}
ARouter.init(mApplication); // 盡可能早锈拨,推薦在Application中初始化
我們直接進(jìn)入ARouter.init方法:
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) { //確保只初始化一次
logger = _ARouter.logger;//日志類
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
變量hasInit
用于保證初始化代碼只執(zhí)行一次;
logger是一個(gè)日志工具類羹唠;
注意_ARouter
類的下劃線奕枢,Arouter
是對(duì)外暴露api的類娄昆,而_ARouter
是真正的實(shí)現(xiàn)類。為什么這樣設(shè)計(jì)那验辞?當(dāng)然是為了解耦啦稿黄。具體點(diǎn)說(shuō)有哪些好處呢喊衫?看看這個(gè)init方法跌造,除了對(duì)實(shí)現(xiàn)類的調(diào)用,還有日志的打印族购,有沒有裝飾模式的感覺壳贪?可以增加一些額外的功能。其次寝杖,對(duì)比_ARouter
類與Arouter
類的方法违施,可以看到明顯Arouter類的方法少。要知道Arouter
是對(duì)外暴露的瑟幕,我們可以有選擇暴露用戶需要的方法磕蒲,而把一些方法隱藏在內(nèi)部。相比于用private修飾只盹,這種方式靈活性更強(qiáng)辣往。
接著我們進(jìn)入實(shí)現(xiàn)類看下:
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
return true;
}
明顯有價(jià)值的是LogisticsCenter.init(mContext, executor);
,executor是一個(gè)線程池殖卑。我們接著來(lái)看下去除日志等無(wú)關(guān)代碼后的LogisticsCenter.init方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
...
Set<String> routerMap;//生成類的類名集合
// 如果是debug模式或者是新版本站削,從apt生成的包中加載類
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {//加入sp緩存
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); //更新版本
} else {//否則從緩存讀取類名
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//判斷類型,使用反射實(shí)例化對(duì)象孵稽,并調(diào)用方法
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
...
}
這里體現(xiàn)了debuggable模式的作用许起,如果沒有開啟debuggable,并且調(diào)試的時(shí)候肯定不會(huì)更改版本號(hào)菩鲜,因此只會(huì)從緩存中讀取類信息园细,所以新添加的路由不會(huì)加載到內(nèi)存中。
ROUTE_ROOT_PAKCAGE
是一個(gè)常量:
public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
可以看到與上面demo生成類的截圖的包名相同接校。而ClassUtils.getFileNameByPackageName
方法做的就是找到app的dex猛频,然后遍歷出其中的屬于com.alibaba.android.arouter.routes
包下的所有類名,打包成集合返回馅笙÷浊牵可以想象遍歷整個(gè)dex查找指定類名的工作量有多大,因此才會(huì)有開頭提到的1.3.0版本新增gradle插件來(lái)代替這個(gè)過程董习。
拿到所有生成類名的集合后烈和,通過反射實(shí)例化對(duì)象并調(diào)用方法,將注解的一些元素添加到static集合中:
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<>();
}
interceptor的map有些特別皿淋,是UniqueKeyTreeMap
招刹,其實(shí)做的很簡(jiǎn)單恬试,僅僅是在key(優(yōu)先級(jí))相同時(shí),拋出指定的異常疯暑。因此训柴,記住不要出現(xiàn)優(yōu)先級(jí)相同的interceptor。
生成的類有統(tǒng)一的命名規(guī)則妇拯,方便區(qū)分幻馁,分別實(shí)現(xiàn)對(duì)應(yīng)的接口:
public interface IRouteRoot {
void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
}
public interface IInterceptorGroup {
void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);
}
public interface IProviderGroup {
void loadInto(Map<String, RouteMeta> providers);
}
public interface IRouteGroup {
void loadInto(Map<String, RouteMeta> atlas);
}
我們來(lái)看下demo生成的實(shí)現(xiàn)類都做了什么:
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
//key為分組名,即路徑的第一段越锈,value為分組中所有的映射關(guān)系
routes.put("service", ARouter$$Group$$service.class);
routes.put("test", ARouter$$Group$$test.class);
}
}
//將module中使用@Route注解的activity或Fragment添加到集合中仗嗦,這里的方法會(huì)在之后調(diào)用
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));
}
}
IRouteRoot的實(shí)現(xiàn)將有@Route注解的module名添加到參數(shù)集合中,也就是groupsIndex甘凭。
這里會(huì)存在一個(gè)小陷阱稀拐,如果不同的module中存在相同的分組(即路徑的第一段,如上面的“test”)丹弱,則會(huì)在對(duì)應(yīng)的module中生成不同的IRouteGroup的實(shí)現(xiàn)德撬,然后在此處會(huì)執(zhí)行分別執(zhí)行
routes.put("test", ARouter$$Group$$moduleA.class);
,以及routes.put("test", ARouter$$Group$$moduleB.class);
躲胳,但是因?yàn)閗ey相同蜓洪,因此前一個(gè)會(huì)被覆蓋,導(dǎo)致前一個(gè)定義的路由無(wú)法找到泛鸟。具體可以看我提的issue蝠咆,官方的建議是路徑分組與模塊名相同,并且不同模塊不要使用相同的分組北滥。
public class ARouter$$Interceptors$$app implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
//key是優(yōu)先級(jí)
interceptors.put(7, Test1Interceptor.class);
}
}
同樣的IInterceptorGroup的實(shí)現(xiàn)將@Interceptor注解的類添加到參數(shù)集合中刚操,也就是interceptorsIndex中。
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, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
}
}
IProviderGroup的實(shí)現(xiàn)將繼承自IProvider的類添加到參數(shù)集合中再芋,也就是providersIndex中菊霜。
RouteMeta是一個(gè)數(shù)據(jù)bean,封裝了被注解類的一些信息
public class RouteMeta {
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
private int priority = -1; // The smaller the number, the higher the priority
private int extra; // Extra data
private Map<String, Integer> paramsType; // Param type
public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
return new RouteMeta(type, null, destination, path, group, paramsType, priority, extra);
}
...
}
其中paramsType是包含了所有注解了Autowired的屬性的信息济赎,key為屬性名鉴逞,value為屬性類型,ARouter將可被intent傳遞的數(shù)據(jù)類型定義了對(duì)應(yīng)的int類型: BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,STRING,PARCELABLE,OBJECT分別對(duì)應(yīng)0司训,1构捡,2,3...
RouteType是一個(gè)枚舉壳猜,表示被注解類的路由類型:
public enum RouteType {
ACTIVITY(0, "android.app.Activity"),
SERVICE(1, "android.app.Service"),
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
BOARDCAST(-1, ""),
METHOD(-1, ""),
FRAGMENT(-1, "android.app.Fragment"),
UNKNOWN(-1, "Unknown route type");
int id;
String className;
RouteType(int id, String className) {
this.id = id;
this.className = className;
}
...
}
可以看到ARouter雖然目前只支持Activity和Fragment勾徽,但是也預(yù)留了Service和Boardcast的類型,可能以后也會(huì)實(shí)現(xiàn)统扳。
最后別忘了init中還有一個(gè)_ARouter.afterInit();
方法:
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
它是一個(gè)管理攔截器的服務(wù)喘帚,實(shí)現(xiàn)了IProvider接口畅姊,并且用自己實(shí)現(xiàn)的的路由方式獲取實(shí)例。嗯吹由,沒毛踩粑础!
根據(jù)官方的說(shuō)明倾鲫,IProvider接口是用來(lái)暴露服務(wù)粗合,并且在初始化的時(shí)候會(huì)被調(diào)用init(Context context)
方法。具體的服務(wù)有其實(shí)現(xiàn)提供级乍,那我們就來(lái)看下它的實(shí)現(xiàn)做了些什么:
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void init(final Context context) {
... //省略子線程以及同步處理舌劳,下面的操作實(shí)際是在子線程處理的
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() + "]");
}
}
...
}
}
還記的在上一步初始化的時(shí)候?qū)⑺凶⒔饬薎nterceptor的類的信息存入了Warehouse.interceptorsIndex,這里就將這些類實(shí)例化玫荣,并調(diào)用iInterceptor.init(context);
完成自定義的初始化內(nèi)容,最后放入Warehouse.interceptors集合中大诸。
總結(jié)來(lái)說(shuō)捅厂,init過程就是把所有注解的信息加載內(nèi)存中,并且完成所有攔截器的初始化资柔。
navigation
完成初始化之后焙贷,就可以實(shí)現(xiàn)路由跳轉(zhuǎn)了:
ARouter.getInstance().build("/test/activity").navigation();
那我們就從這里開始分析:
getInstance()
是獲取ARouter類的單例方法,沒什么好說(shuō)的贿堰。
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
build方法調(diào)動(dòng)了實(shí)現(xiàn)類_ARouter的build方法辙芍,繼續(xù)看:
protected Postcard build(String path) {
...//省略判空
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
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);
}
}
這里出現(xiàn)一個(gè)PathReplaceService,它是繼承IProvider的接口羹与,它是預(yù)留給用戶實(shí)現(xiàn)路徑動(dòng)態(tài)變化功能故硅,官方有詳細(xì)說(shuō)明:
public interface PathReplaceService extends IProvider {
String forString(String path);
Uri forUri(Uri uri);
}
需要注意的是,build(String path)
方法及build(String path, String group)
方法中都嘗試使用PathReplaceService完成路徑動(dòng)態(tài)變化纵搁,因此吃衅,在變化前需要做好判斷,否則出現(xiàn)變換兩次的情況(例如拼接字符)腾誉。
extractGroup方法截取路徑中的第一段作為分組名徘层。
build方法最終返回一個(gè)Postcard對(duì)象。它也是一個(gè)數(shù)據(jù)bean利职,繼承自RouteMeta趣效,附加一些跳轉(zhuǎn)信息,如參數(shù)之類:
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong.
private Bundle mBundle; // Data to transform
private int flags = -1; // Flags of route
private int timeout = 300; // Navigation timeout, TimeUnit.Second
private IProvider provider; // It will be set value, if this postcard was provider.
private boolean greenChannel;
private SerializationService serializationService;
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
...
}
最后調(diào)用該P(yáng)ostcard對(duì)象的navigation方法猪贪,層層調(diào)用跷敬,最終還是調(diào)用的_Arouter
的navigation方法,我們先來(lái)看下方法的前半部分:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
if (null != callback) {
callback.onLost(postcard);
} else { // 交給全局降級(jí)策略
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
... //方法未結(jié)束哮伟,分到之后
}
大致可看出LogisticsCenter.completion(postcard);
肯定是試圖找到跳轉(zhuǎn)的目標(biāo)干花,如果找不到則讓callback回調(diào)onLost妄帘,或者交給全局降級(jí)策略處理。找到則回調(diào)callback的onFound方法池凄。
那我們看看LogisticsCenter.completion(postcard);
是如何尋找的:
public synchronized static void completion(Postcard postcard) {
...
//從集合中找路徑對(duì)應(yīng)的RouteMeta
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // 內(nèi)存中沒有抡驼,可能是還沒有加載過
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // 找到分組
if (null == groupMeta) {//分組也沒有,那就是沒有注冊(cè)
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else { // 加載具體分組的映射肿仑,并從groupsIndex集合中刪除類信息
try {
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // 遞歸再次嘗試加載
}
} else {
//拷貝數(shù)據(jù)
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // 如果是Uri跳轉(zhuǎn)
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);//分割路徑中的參數(shù)
Map<String, Integer> paramsType = routeMeta.getParamsType();//獲取Autowired注解的屬性
if (MapUtils.isNotEmpty(paramsType)) { //將屬性對(duì)應(yīng)的參數(shù)值放入Bundle
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Bundle存入需要自動(dòng)注入的屬性信息
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// 保存原始路徑
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: //如果目標(biāo)類型是provider致盟,實(shí)例化對(duì)象并放入postcard
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);//初始化在這里調(diào)用
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider不經(jīng)過攔截器處理
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment不僅過攔截器處理
default:
break;
}
}
}
可以看到主要功能是找到匹配的目標(biāo)類,并將目標(biāo)類的一些信息拷貝到postcard對(duì)象中尤慰。
我們繼續(xù)看navigation方法的后半部分:
protected Object navigation(final Context context, final Postcard postcard, final int request
Code, final NavigationCallback callback) {
...
if (!postcard.isGreenChannel()) { //不是綠色通道馏锡,即經(jīng)過攔截器處理
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else { //綠色通道
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
_navigation
方法是最終處理,并且有返回值伟端,如果路由目標(biāo)是Fragment或者IProvider的話杯道。攔截器的處理可能會(huì)耗時(shí),因此會(huì)放到子線程處理责蝠,通過回調(diào)完成繼續(xù)操作党巾,但此時(shí)就無(wú)法返回目標(biāo)類(Fragment或IProvider),也就解釋了為什么上面的completion方法中設(shè)置了綠色通道霜医。
我們先不考慮攔截過程齿拂,直接看_navigation
方法:
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//mContext是init傳入的Application
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);
}
//攔截過程在子線程,因此該方法可能仍在子線程肴敛,需要切換到主線程
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 ((-1 != postcard.getEnterAnim() && -1 != 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:
//LogisticsCenter.completion方法中存入的
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;
}
navigation可以傳入Context或者不傳署海,不傳的話默認(rèn)就用Application的context,每開啟一個(gè)Activity就會(huì)新開一個(gè)task医男。建議普通界面跳轉(zhuǎn)都傳入Activity砸狞,避免棧的混亂。
至此整個(gè)路由跳轉(zhuǎn)的過程大致完成昨登,整個(gè)過程可以分為:封裝Postcard -> 查找信息集合趾代,實(shí)例化目標(biāo)類 -> 返回實(shí)例或者跳轉(zhuǎn)。
Interceptor
前面我們跳過了攔截過程丰辣,現(xiàn)在我們來(lái)分析下這個(gè)過程撒强,攔截功能是通過ARouter提供的interceptorService實(shí)現(xiàn)的,并且之前我們?cè)趇nit章節(jié)分析它的初始化笙什,接下來(lái)看看具體是如何攔截的:
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
可以看到調(diào)用了doInterceptions方法飘哨,在看這個(gè)方法之前,我們先要了解一個(gè)類:CountDownLatch琐凭。相關(guān)資料:CountDownLatch理解一:與join的區(qū)別
簡(jiǎn)單來(lái)說(shuō)CountDownLatch可以阻塞一個(gè)線程芽隆,知道內(nèi)部計(jì)數(shù)器為0時(shí),才繼續(xù)執(zhí)行阻塞的線程,計(jì)數(shù)器的初始值通過構(gòu)造傳入胚吁,通過調(diào)用countDown()方法減少一個(gè)計(jì)數(shù)牙躺。
攔截器的方法中,其中就使用CancelableCountDownLatch類腕扶,它繼承自CountDownLatch孽拷,并擴(kuò)展了cancel方法,用于直接將計(jì)數(shù)歸0半抱,放開阻塞:
public void cancel() {
while (getCount() > 0) {
countDown();
}
}
我們回到攔截器的doInterceptions方法:
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
... //省略同步等待初始化
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_excute(0, interceptorCounter, postcard);
//阻塞線程直到超時(shí)脓恕,或者計(jì)數(shù)歸0
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { //攔截超時(shí)
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // 被攔截
callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else { //放行
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* Excute interceptor
*
* @param index current interceptor index
* @param counter interceptor counter
* @param postcard routeMeta
*/
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
//有下一個(gè)攔截器
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// 如果放行,則計(jì)數(shù)減1窿侈,執(zhí)行后一個(gè)攔截器
counter.countDown();
_excute(index + 1, counter, postcard);
}
@Override
public void onInterrupt(Throwable exception) {
// 攔截炼幔,將exception存入postcard的tag字段,計(jì)數(shù)歸零
postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());
counter.cancel();
});
}
}
首先會(huì)創(chuàng)建一個(gè)與攔截器數(shù)量相同的CancelableCountDownLatch初始計(jì)數(shù)值史简,每放行一個(gè)攔截器就countDown乃秀,并交給后一個(gè)攔截器,如果攔截則清0計(jì)數(shù)乘瓤,并將攔截的Throwable存入postcard的tag字段环形,interceptorCounter.await();
阻塞直到計(jì)數(shù)歸0或者阻塞超時(shí)(默認(rèn)是300秒),最后通過interceptorCounter.getCount()
判斷是否是超時(shí)衙傀,還是攔截或者放行。
可以看到攔截的過程都是在子線程中處理萨咕,包括Interceptor的process也是在子線程調(diào)用的统抬,因此,如果想要在攔截過程中展示dialog等都需要切換到主線程危队。
inject
通過@Autowired注解的屬性聪建,通過調(diào)用ARouter.getInstance().inject(this);
可以實(shí)現(xiàn)自動(dòng)注入。我們知道原生的Activity傳遞數(shù)據(jù)是通過Bundle攜帶的茫陆。因此金麸,ARouter的數(shù)據(jù)傳遞肯定也是基于Bundle,并實(shí)現(xiàn)了自動(dòng)賦值的功能簿盅。
同樣的挥下,我們從方法入口層層深入探究:
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
AutowiredService與InterceptorService類似,都是Arouter自己實(shí)現(xiàn)的繼承IProvider接口的服務(wù)桨醋。我們找到AutowiredService的實(shí)現(xiàn)類:
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
private LruCache<String, ISyringe> classCache;
private List<String> blackList;
@Override
public void init(Context context) {
classCache = new LruCache<>(66);
blackList = new ArrayList<>();
}
@Override
public void autowire(Object instance) {
String className = instance.getClass().getName();
try {
if (!blackList.contains(className)) {
ISyringe autowiredHelper = classCache.get(className);
if (null == autowiredHelper) { // No cache.
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
autowiredHelper.inject(instance);
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
blackList.add(className); // This instance need not autowired.
}
}
}
去掉緩存集合之后棚瘟,其實(shí)很簡(jiǎn)單,核心內(nèi)容就兩句:
ISyringe autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
autowiredHelper.inject(instance);
ISyringe是一個(gè)接口:
public interface ISyringe {
void inject(Object target);
}
它的實(shí)現(xiàn)是apt生成的喜最,每一個(gè)帶有@Autowired注解的類都會(huì)生成一個(gè)對(duì)應(yīng)的ISyringe的實(shí)現(xiàn)偎蘸,如demo中的:
//生成類的類名 = 目標(biāo)類名+ "$$ARouter$$Autowired"(固定后綴)
public class BlankFragment$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
//強(qiáng)轉(zhuǎn)成目標(biāo)類
BlankFragment substitute = (BlankFragment)target;
//給每個(gè)注解了@Autowired的屬性賦值
//這里的key為屬性名或者注解中給定的name
substitute.name = substitute.getArguments().getString("name");
// 如果存在Bundle無(wú)法攜帶的類型,則會(huì)通過SerializationService序列化成json的String傳遞,SerializationService沒有提供默認(rèn)實(shí)現(xiàn)迷雪,需要用戶自己實(shí)現(xiàn)
if (null != serializationService) {
substitute.obj = serializationService.parseObject(substitute.getArguments().getString("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'BlankFragment' , then you should implement 'SerializationService' to support object auto inject!");
}
if (null == substitute.obj) {
Log.e("ARouter::", "The field 'obj' is null, in class '" + BlankFragment.class.getName() + "!");
}
}
}
注意這里賦值的操作是直接調(diào)用“目標(biāo)類對(duì)象.屬性”的方式賦值,因此,private修飾的屬性無(wú)法通過這種方式賦值限书,并且在賦值時(shí)會(huì)拋出異常,被AutowiredServiceImpl的autowire方法中的try-catch捕獲章咧,存入不需要注入的集合中倦西,最終導(dǎo)致同一個(gè)類中的其他非private屬性也無(wú)法注入。
其注入原理基本與ButterKnife類似慧邮。前面分析navigation的時(shí)候我們看到了將Uri參數(shù)存入Bundle的過程(或者由用戶手動(dòng)調(diào)用withXX存入bundle)调限,此處則是將Bundle中的數(shù)據(jù)取出,并賦值給@Autowired注解的屬性误澳。