相關(guān)角色:
ARouter:負(fù)責(zé)提供客戶端使用的Api接口闭专,采用了門面模式,實(shí)際上內(nèi)部委托給了_ARouter去處理
_ARouter:路由中心控制器,負(fù)責(zé)控制整個(gè)路由的流程纹烹,通過(guò)Postcard中的信息導(dǎo)航客戶端到目標(biāo)地址(啟動(dòng)某個(gè)Activity或者獲取某個(gè)服務(wù)的實(shí)現(xiàn)等)
LogisticsCenter:后勤中心,負(fù)責(zé)注冊(cè)路由信息到Warehouse和根據(jù)path或者Postcard到數(shù)據(jù)倉(cāng)庫(kù)中獲取數(shù)據(jù)召边,再生成相關(guān)對(duì)象
Warehouse:數(shù)據(jù)倉(cāng)庫(kù)铺呵,負(fù)責(zé)存儲(chǔ)路由配置信息和具體生成的IProvider對(duì)象等。該類基本上都是一些數(shù)據(jù)集合隧熙,沒(méi)有任何邏輯處理
Postcard:明信片片挂,RouteMeta類的子類,用于描述一個(gè)路由的具體信息贞盯,比如音念,目標(biāo)組件類型(Activity||IProvider等)、目標(biāo)組件需要的參數(shù)躏敢,
RouteMeta:路由信息描述類闷愤,存儲(chǔ)目標(biāo)地址的類型,路徑件余,參數(shù)等信息讥脐,LogisticsCenter根據(jù)RouteMeta對(duì)象描述的信息創(chuàng)建明信片遭居。
IRouteGroup:多個(gè)RouteMeta數(shù)據(jù)的容器,類似ViewGroup與View的關(guān)系
IProvider:服務(wù)提供者旬渠,每一個(gè)實(shí)現(xiàn)該接口的類視為一個(gè)獨(dú)立的服務(wù)俱萍,外部客戶端可以根據(jù)path獲取到該服務(wù)。
IInterceptor:攔截器告丢,客戶端可以通過(guò)注冊(cè)IInterceptor的實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)路由的攔截枪蘑,其攔截流程控制是在子線程中按照注冊(cè)順序依次調(diào)用攔截器的process方法將攔截權(quán)釋放給客戶端。其攔截控制器的實(shí)現(xiàn)在InterceptorServiceImpl類中岖免。
PathReplaceService:路徑替換服務(wù)接口岳颇,實(shí)現(xiàn)者需要將path轉(zhuǎn)換為另一個(gè)path
初始化流程:
ARouter框架能將多個(gè)服務(wù)提供者隔離,減少相互之間的依賴颅湘。其實(shí)現(xiàn)的流程和我們平常的快遞物流管理很類似赦役,每一個(gè)具體的快遞包裹就是一個(gè)獨(dú)立的服務(wù)提供者(IProvider),每一個(gè)快遞信息單就是一個(gè)RouteMeta對(duì)象栅炒,客戶端就是快遞的接收方掂摔,而使用@Route注解中的path就是快遞單號(hào)。在初始化流程中赢赊,主要完成的工作就是將所有注冊(cè)的快遞信息表都在物流中心(LogisticsCenter)注冊(cè)乙漓,并將數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)倉(cāng)庫(kù)中(Warehouse)。
初始化的入口是ARouter的init方法释移,其主要是控制初始化的流程叭披,自身不處理具體實(shí)現(xiàn),而是都委托給_ARouter去處理玩讳。
public static void init(Application application) {
if (!hasInit) {
//委托給_ARouter去初始化
hasInit = _ARouter.init(application);
if (hasInit) {
//初始化之后調(diào)用afterInit
_ARouter.afterInit();
}
}
}
_ARouter方法的init方法實(shí)際是調(diào)用LogisticsCenter的init方法涩蜘,下面是其核心代碼:
Set<String> routerMap;
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
//掃描apk中所有類,找到ROUTE_ROOT_PAKCAGE包下的類熏纯,實(shí)在子線程中完成的
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<String>()));
}
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)) {
// 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);
}
}
LogisticsCenter的init方法主要完成了下面幾件事:
- 找到com.alibaba.android.arouter.routes包下的所有class文件類名同诫,如果本地緩存的數(shù)據(jù)有效就從本地獲取,如果有更新或者是debug模式樟澜,則通過(guò)掃描安裝包的dex文件獲取
- 根據(jù)找到的類名去加載相關(guān)的實(shí)例到Warehouse中(類似與快遞信息表入庫(kù))
實(shí)際上com.alibaba.android.arouter.routes包下的class是由注解解析器自動(dòng)生成的误窖,主要IRouteRoot,IRouteGroup和IProviderGroup的實(shí)現(xiàn)類秩贰,比如當(dāng)我們使用@Route注解某個(gè)類時(shí)霹俺,會(huì)自動(dòng)將這個(gè)類的信息注入的到自動(dòng)生成的上述實(shí)現(xiàn)類中。
完成初始化之后會(huì)調(diào)用afterInit方法毒费,其主要就是注入攔截控制服務(wù)(InterceptorServiceImpl)
static void afterInit() {
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
//build方法主要是將path和group封裝到Postcard中丙唧,可以理解成根據(jù)快遞號(hào)生成一個(gè)快遞信息表
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);
}
}
而navigation方法是根據(jù)快遞信息表來(lái)生成具體的實(shí)例,這里的攔截服務(wù)控制器實(shí)際是InterceptorServiceImpl對(duì)象觅玻,后面分析具體路由的實(shí)現(xiàn)時(shí)再看其具體的代碼實(shí)現(xiàn)想际。整個(gè)初始化流程到注入InterceptorServiceImpl后就基本完成了
如何實(shí)現(xiàn)路由功能:
這里以一個(gè)具體的使用場(chǎng)景來(lái)看下路由的具體實(shí)現(xiàn)培漏,我們實(shí)現(xiàn)了一個(gè)微博分享的服務(wù),并使用@Route標(biāo)注該服務(wù)沼琉。如下:
@Route(path = "/service/WBShareService")
public class WBShareServiceImp implements IShareService extends IProvider{
@Override
public void doShareImage(String text, String title, String path, boolean onlyClient) {
}
}
然后客戶端需要分享圖片到微博時(shí)的使用代碼如下:
Object obj = ARouter.getInstance().build("/service/WBShareService").navigation();
if (obj instanceof IShareService) {
((IShareService)obj).doShareImage("", "", "", false);
}
前面我們分析過(guò)ARouter的build方法了,其就是根據(jù)path生成一個(gè)Postcard對(duì)象桩匪,這里我們接著分析navigation方法打瘪,postcard對(duì)象的navigation方法最終都是委托給_ARouter的navigation方法來(lái)處理。
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//到物流中心完成postcard的信息填充傻昙,因?yàn)樽畛跎傻膒ostcard對(duì)象只包含path信息闺骚,不包含其他有效信息,比如路由類型妆档,攜帶的參數(shù)等
LogisticsCenter.completion(postcard);
//如果不是綠色通道僻爽,則通過(guò)攔截控制器依次調(diào)用不同的攔截器處理信息(類似與一個(gè)包裹在檢查通道了經(jīng)過(guò)多個(gè)掃描檢查)
if (!postcard.isGreenChannel()) {
//每個(gè)攔截器的攔截方法調(diào)用都是在子線程中執(zhí)行的
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) {
//只要有一個(gè)攔截器攔截該包裹,則回調(diào)onInterrupt方法宣告本次路由被終止
callback.onInterrupt(postcard);
}
}
});
} else {
//綠色通道則直接調(diào)用_navigation方法進(jìn)行具體的導(dǎo)航
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
可以看出navigation方法主要做了如下事情:
- 根據(jù)只包含path(理解成只有快遞單號(hào)的快遞信息表)的postcard去物流中心查找具體的路由信息(由編譯時(shí)生成贾惦,在init時(shí)注入)胸梆,完成后續(xù)步驟需要的數(shù)據(jù)填充。
- 如果不是綠色通道须板,則將postcard交予攔截控制器碰镜,委托各個(gè)攔截器在子線程執(zhí)行檢查是否攔截。
- 如果未攔截习瑰,則執(zhí)行具體的導(dǎo)航功能
這里先看下LogisticsCenter是怎么去填充信息到postcard中:
public synchronized static void completion(Postcard postcard) {
//去數(shù)據(jù)倉(cāng)庫(kù)獲取路由信息绪颖,該信息在初始化ARouter時(shí)已經(jīng)注入
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
//如果沒(méi)有路由信息,則嘗試去數(shù)據(jù)倉(cāng)庫(kù)查找
} else {
//找到路由信息后甜奄,則將配置的路由信息填充到Postcard對(duì)象中
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.
//這里主要是完成參數(shù)的填充
}
//針對(duì)不同的路由類型進(jìn)行處理
switch (routeMeta.getType()) {
case PROVIDER:
//如果是服務(wù)提供者柠横,則嘗試獲取其具體實(shí)例,如果沒(méi)有课兄,則根據(jù)路由信息構(gòu)造一個(gè)實(shí)例牍氛,初始化并存儲(chǔ)到數(shù)據(jù)倉(cāng)庫(kù),
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;
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
}
postcard.setProvider(instance);
//服務(wù)提供者被設(shè)置成綠色渠道烟阐,不用接受攔截檢查
postcard.greenChannel();
break;
case FRAGMENT:
//fragment也不用攔截檢查
postcard.greenChannel();
default:
break;
}
}
}
信息填充完之后糜俗,看一下具體的路由實(shí)現(xiàn):
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;
}
從上述代碼我們可以看出,不同類型的路由其導(dǎo)航的方式也不一樣
- 如果是Activity類型曲饱,則將數(shù)據(jù)填充到intent中之后悠抹,調(diào)用ActivityCompat的startActivity或者startActivityForResult方法啟動(dòng)activity。
- 如果是PROVIDER類型扩淀,則直接返回其服務(wù)提供者
- 如果是BOARDCAST || CONTENT_PROVIDER || FRAGMENT楔敌,則創(chuàng)建其需要的實(shí)體,并填充數(shù)據(jù)驻谆,再返回該實(shí)體對(duì)象