ARouter源碼解讀
以前看優(yōu)秀的開(kāi)源項(xiàng)目绕德,看到了頁(yè)面路由框架ARouter,心想頁(yè)面路由是個(gè)啥東東凡蚜,于是乎網(wǎng)上搜索查看人断,是阿里出品開(kāi)源的,主要是關(guān)于頁(yè)面跳轉(zhuǎn)的解耦框架朝蜘。一直想看看具體是怎么實(shí)現(xiàn)的恶迈,今有時(shí)間便來(lái)一探究竟。
傳統(tǒng)的頁(yè)面跳轉(zhuǎn)就是調(diào)用系統(tǒng)的startActivity谱醇,里面的參數(shù)Intent攜帶了要跳轉(zhuǎn)的信息暇仲,可以傳入要跳轉(zhuǎn)的activity信息或者action。如果是action則要在清單文件里面配置副渴。但是ARouter的實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)則另辟蹊徑,傳入一個(gè)path路徑奈附,官方示例代碼如下
ARouter.getInstance().build("/test/activity2") .navigation();
初看到這樣的代碼你會(huì)想,這是要跳轉(zhuǎn)到哪里去呢?既然只有一個(gè)路徑佳晶,那么某個(gè)activity必然會(huì)和這個(gè)路徑有關(guān)系.果然在示例代碼的Test2Activity上面有個(gè)注解,里面就有個(gè)這個(gè)參數(shù)桅狠。
基于以前注解的知識(shí)讼载,作猜想,只要獲取Activity上面的注解參數(shù)轿秧,再把該參數(shù)和該Activity綁定起來(lái),全局緩存咨堤,只要匹配跳轉(zhuǎn)路徑是該Activity上注解參數(shù)菇篡,就讓它跳轉(zhuǎn)到該Activity.大概應(yīng)該是這樣。有了這個(gè)猜想一喘,再去查看源碼驱还,看看是否符合猜想。下面開(kāi)始探索源碼之旅凸克。
首先調(diào)用其初始化方法
ARouter.init(getApplication());
點(diǎn)到這個(gè)ARouter類init方法里面议蟆,發(fā)現(xiàn)里面調(diào)用的是一個(gè)_ARouter的初始化方法,再初看其他方法萎战,發(fā)現(xiàn)所有方法其實(shí)都是調(diào)用的_ARouter類的方法咐容。
看_ARouter類的初始化方法
protected static synchronized boolean init(Application application) {????
mContext = application;???
LogisticsCenter.init(mContext, executor);//LogisticsCenter類的初始化????
logger.info(Consts.TAG, "ARouter init success!");//日志初始化???
?hasInit = true;//根據(jù)該變量判斷是否初始化了,若使用前未調(diào)用初始化方法蚂维,則拋出未初始化異常信息 ????// It's not a good idea.????//
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {????// ????application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());????
// }???
?return true;
}
其主要是LogisticsCenter類的初始化戳粒。
LogisticsCenter類的初始化主要都做了什么事情呢路狮?
繼續(xù)往下看
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {???...前面省略無(wú)關(guān)代碼
Set 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.//獲取Arouter自動(dòng)生成的類的信息???????????
?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();????????????}????????????
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()));????????}????????
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");????????startInit = System.currentTimeMillis();
//將路徑按分類加入緩存???????
?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);????????????}????????}...后面省略無(wú)關(guān)代碼??????}
執(zhí)行完getFileNameByPackageName這個(gè)方法后,Set里面都有些什么呢?
緩存類
初始化完畢蔚约。
小結(jié):初始化方法只是要把緩存里面的groupsIndex和providerIndex和intercaptorsIndex三個(gè)Map集合先緩存了奄妨。groupsIndex主要是組名的路徑和對(duì)象class映射。什么是組名的路徑呢苹祟?
比如當(dāng)前示例傳入的路徑為/test/activity2,則組名就是test砸抛。providerIndex主要是用于依賴注入的path路徑和class對(duì)應(yīng)的關(guān)系。至于啥事依賴注入树枫,就是聲明一個(gè)對(duì)象,并不實(shí)例化锰悼,由框架根據(jù)你傳入的參數(shù)生成對(duì)應(yīng)的對(duì)象,說(shuō)白點(diǎn)就是通過(guò)框架實(shí)例化你所需要的對(duì)象(試想連對(duì)象都不用自己實(shí)例化了,是不是耦合性就非常低了)团赏。如果知道java web spring 框架的話箕般,那么這個(gè)就很清楚了,因?yàn)閟pring框架里面就大量應(yīng)用了依賴注入舔清。InterceptorsIndex則是保存了攔截器的path和class對(duì)應(yīng)的關(guān)系丝里,何為攔截器,攔截什么操作体谒?杯聚,繼續(xù)看下面代碼。
現(xiàn)在查看調(diào)用代碼抒痒,
ARouter.getInstance()????????.build("/test/activity2")????????.navigation();
Build方法調(diào)用_ARouter方法的build方法
public Postcard build(String path) {????return _ARouter.getInstance().build(path);}
查看_ARouter方法
/**?* Build postcard by path and default group?*/
protected Postcard build(String path) {????
if (TextUtils.isEmpty(path)) {????????t
hrow new HandlerException(Consts.TAG + "Parameter is invalid!");???
?} else {????????
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);????????
if (null != pService) {????????????path = pService.forString(path);????????}????????
return build(path, extractGroup(path));????}}
其中extractGroup方法是根據(jù)路徑解析其路徑所在的組幌绍。
/**?* Extract the default group from path.?*/
private String extractGroup(String path) {????
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {????????throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");????
}????try {
//如果當(dāng)前路徑是/xxx/... 則默認(rèn)其在xxx組內(nèi). ????????
String defaultGroup = path.substring(1, path.indexOf("/", 1));???????
?if (TextUtils.isEmpty(defaultGroup)) {????????????
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");????????
} else {????????????return defaultGroup;????????}????
} catch (Exception e) {???????
?logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());????????return null;????}}
最終調(diào)用這個(gè)方法
/**?* Build postcard by path and group?*/
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);????}}
這個(gè)方法后返回一個(gè)新的Postcard對(duì)象。
先查看Postcard對(duì)象的navigation方法,里面又多個(gè)重載方法故响,但最后都調(diào)用了此方法傀广。
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {????
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);}
還是調(diào)用_ARouter的navigation方法
查看navigation方法
/**?* 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());
..//后面代碼暫時(shí)省略
/**?* Completion the postcard by route metas
?*?*@param postcard Incomplete postcard, should completion by this method.?*/
public synchronized static void completion(Postcard postcard) {????????
//從緩存中獲取path對(duì)應(yīng)的RouteMeta類型????//RouteMeta中保存了該路徑對(duì)應(yīng)的目標(biāo)的Class類型????RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());???
?//如果沒(méi)有,去加載????if (null == routeMeta) { ???
// Maybe its does't exist, or didn't load.????????//查找所在組群的類信息???????
?Class 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.???????????????
?IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//加載該群組下面的路徑和class對(duì)應(yīng)的信息????????????????
iGroupInstance.loadInto(Warehouse.routes);????????????????
Warehouse.groupsIndex.remove(postcard.getGroup());????????????????????
completion(postcard); ??// Reload????????}????}
else {????????
postcard.setDestination(routeMeta.getDestination()); ???
//設(shè)置跳轉(zhuǎn)的class信息???????
?postcard.setType(routeMeta.getType()); ?????????????//設(shè)置路由類型????????postcard.setPriority(routeMeta.getPriority()); ?????//路由優(yōu)先級(jí)????????postcard.setExtra(routeMeta.getExtra());????????
Uri rawUri = postcard.getUri();???????
?if (null != rawUri) { ??// Try to set params into bundle.????????????
Map resultMap = TextUtils.splitQueryParameters(rawUri);????????????
Map paramsType = routeMeta.getParamsType();????????????if (MapUtils.isNotEmpty(paramsType)) {????????????????// Set value by its type, just for params which annotation by @Param????????????????for (Map.Entry 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());????????}...//如果是acitivty跳轉(zhuǎn),下面代碼省略暫時(shí)不看???????}
該方法主要是設(shè)置postcard對(duì)象里面的要跳轉(zhuǎn)的信息彩届,和跳轉(zhuǎn)時(shí)如果有參數(shù)則設(shè)置參數(shù)伪冰。
那么如何使將path和對(duì)應(yīng)的class類信息加載進(jìn)來(lái)的呢?
通過(guò)調(diào)試可以發(fā)現(xiàn)是調(diào)用了ARouter$$Group$$test的loadInto方法
找到該類的該方法,loadInto方法里面有設(shè)置路徑和對(duì)應(yīng)的RoteMeta對(duì)象一一對(duì)應(yīng)樟蠕,而RoteMeta對(duì)象則是保存了相應(yīng)的屬性,比如類型,和跳轉(zhuǎn)的目標(biāo)類類型贮聂。該類沒(méi)有在src目錄下,而是在build目標(biāo)下寨辩,由此可見(jiàn)該類是自動(dòng)編譯的吓懈。
但是是如何自動(dòng)編譯的呢,這個(gè)問(wèn)題暫時(shí)放下靡狞。
繼續(xù)往下看耻警,既然已經(jīng)找到了path和要跳轉(zhuǎn)的activity class的信息了,那么接下應(yīng)該就是調(diào)用真正的跳轉(zhuǎn)方法了。繼續(xù)navigation方法往下看.
/**?* 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) {
//省略前面代碼...
//如果設(shè)置了greenChannel為true榕栏,表示不需要攔截畔勤,為false。則跳轉(zhuǎn)前還需做一步攔截操作????????if (!postcard.isGreenChannel()) { ?
// It must be run in async thread, maybe interceptor cost too mush time made ANR.
//前面初始化分析的攔截器作用就在此???????
interceptorService.doInterceptions(postcard, new InterceptorCallback() {???????????????????????
@Override???????????
public void onContinue(Postcard postcard) {??????????????
?_navigation(context, postcard, requestCode, callback);????????????}??????????????????});??
?} else {???????
return _navigation(context, postcard, requestCode, callback);??
?}????return null;}
最后調(diào)用了_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());//設(shè)置要跳轉(zhuǎn)攜帶的參數(shù)????????????// 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 {
//真正的跳轉(zhuǎn)動(dòng)作????????????????????????
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());????????????????????}???????????????????????????????});????????????
break;???????//如果類型是acitivity則庆揪,后面的可以省略不看????return null;}
終于看到了startActivity方法了。
查看跳轉(zhuǎn)到Test1Activity的示例代碼時(shí)妨托,里面攜帶了要傳入Test1Activity的參數(shù)缸榛,但是Test1Activity里面沒(méi)有看到獲取參數(shù)代碼,而是每個(gè)參數(shù)上面還有@Autowited注解
@Route(path = "/test/activity1")public class Test1Activity extends AppCompatActivity {????@Autowired????String name = "jack";
還有onCreate方法里面的調(diào)用了inject方法.由此可猜測(cè)該方法應(yīng)該已經(jīng)把@Autowited下面的變量全都賦值了兰伤。
ARouter.getInstance().inject(this);
追蹤查看該方法最終調(diào)用了_ARouter里面的inject方法
static void inject(Object thiz) {????
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());????
if (null != autowiredService) {????????autowiredService.autowire(thiz);????}}
AutowiredService 是個(gè)接口内颗,其實(shí)現(xiàn)是AutowiredServiceImpl類
查看AutowiredServiceImpl類的autowite方法
@Overridepublic void autowire(Object instance) {????
String className = instance.getClass().getName();????
try {????????if (!blackList.contains(className)) {
//????????????ISyringe autowiredHelper = classCache.get(className);????????????
if (null == autowiredHelper) { ?// No cache.
//查找對(duì)應(yīng)的ISyringe 實(shí)現(xiàn)類,className$$ARouter$$Autowired
????????????????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.????}}
最終調(diào)用了className$$ARouter$$Autowired類里面的inject
方法,看到這個(gè)類的類名字敦腔,在該工程查找該類均澳,發(fā)現(xiàn)也在build目錄下
可以看到最終這里調(diào)用getIntent().getXXX獲取Intent攜帶的參數(shù)。因?yàn)閹缀跛蝎@取攜帶參數(shù)的代碼方式是一樣的符衔,因此這里直接也是用自動(dòng)編譯而成的找前。
小結(jié):梳理下跳轉(zhuǎn)界面的步驟 1 首先根據(jù)路徑生成一個(gè)路徑和組名的Postcard對(duì)象。2查看緩存里面是否有組名的class信息判族,如果沒(méi)有躺盛,根據(jù)組名獲取所有組名下面的的class信息。3 根據(jù)獲取的信息填充postcard對(duì)象形帮。4 如果需要攔截槽惫,如執(zhí)行攔截器的方法。5 如果沒(méi)有攔截辩撑,執(zhí)行跳轉(zhuǎn)動(dòng)作. 6 跳轉(zhuǎn)的那個(gè)activity執(zhí)行注入?yún)?shù)方法界斜。
總結(jié):ARouter的界面跳轉(zhuǎn)流程分析完畢。但是ARouter的用處不止界面跳轉(zhuǎn)槐臀,還有依賴注入等其他功能,根據(jù)github官網(wǎng)介紹
1. 支持直接解析標(biāo)準(zhǔn)URL進(jìn)行跳轉(zhuǎn)锄蹂,并自動(dòng)注入?yún)?shù)到目標(biāo)頁(yè)面中
2. 支持多模塊工程使用
3. 支持添加多個(gè)攔截器,自定義攔截順序
4. 支持依賴注入水慨,可單獨(dú)作為依賴注入框架使用
5. 支持InstantRun
6.支持MultiDex(Google方案)
7.映射關(guān)系按組分類、多級(jí)管理敬扛,按需初始化
8. 支持用戶指定全局降級(jí)與局部降級(jí)策略
9. 頁(yè)面晰洒、攔截器、服務(wù)等組件均自動(dòng)注冊(cè)到框架
10. 支持多種方式配置轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
11. 支持獲取Fragment
12. 完全支持Kotlin以及混編(配置見(jiàn)文末 其他#5)
有興趣可以自行查看其他功能詳解
ARouter github地址https://github.com/alibaba/ARouter