相关角色:
ARouter:负责提供客户端使用的Api接口,采用了门面模式,实际上内部委托给了_ARouter去处理
_ARouter:路由中心控制器,负责控制整个路由的流程,通过Postcard中的信息导航客户端到目标地址(启动某个Activity或者获取某个服务的实现等)
LogisticsCenter:后勤中心,负责注册路由信息到Warehouse和根据path或者Postcard到数据仓库中获取数据,再生成相关对象
Warehouse:数据仓库,负责存储路由配置信息和具体生成的IProvider对象等。该类基本上都是一些数据集合,没有任何逻辑处理
Postcard:明信片,RouteMeta类的子类,用于描述一个路由的具体信息,比如,目标组件类型(Activity||IProvider等)、目标组件需要的参数,
RouteMeta:路由信息描述类,存储目标地址的类型,路径,参数等信息,LogisticsCenter根据RouteMeta对象描述的信息创建明信片。
IRouteGroup:多个RouteMeta数据的容器,类似ViewGroup与View的关系
IProvider:服务提供者,每一个实现该接口的类视为一个独立的服务,外部客户端可以根据path获取到该服务。
IInterceptor:拦截器,客户端可以通过注册IInterceptor的实现类来实现路由的拦截,其拦截流程控制是在子线程中按照注册顺序依次调用拦截器的process方法将拦截权释放给客户端。其拦截控制器的实现在InterceptorServiceImpl类中。
PathReplaceService:路径替换服务接口,实现者需要将path转换为另一个path
初始化流程:
ARouter框架能将多个服务提供者隔离,减少相互之间的依赖。其实现的流程和我们平常的快递物流管理很类似,每一个具体的快递包裹就是一个独立的服务提供者(IProvider),每一个快递信息单就是一个RouteMeta对象,客户端就是快递的接收方,而使用@Route注解中的path就是快递单号。在初始化流程中,主要完成的工作就是将所有注册的快递信息表都在物流中心(LogisticsCenter)注册,并将数据存储到数据仓库中(Warehouse)。
初始化的入口是ARouter的init方法,其主要是控制初始化的流程,自身不处理具体实现,而是都委托给_ARouter去处理。
public static void init(Application application) {
if (!hasInit) {
//委托给_ARouter去初始化
hasInit = _ARouter.init(application);
if (hasInit) {
//初始化之后调用afterInit
_ARouter.afterInit();
}
}
}
_ARouter方法的init方法实际是调用LogisticsCenter的init方法,下面是其核心代码:
Set<String> routerMap;
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
//扫描apk中所有类,找到ROUTE_ROOT_PAKCAGE包下的类,实在子线程中完成的
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文件类名,如果本地缓存的数据有效就从本地获取,如果有更新或者是debug模式,则通过扫描安装包的dex文件获取
- 根据找到的类名去加载相关的实例到Warehouse中(类似与快递信息表入库)
实际上com.alibaba.android.arouter.routes包下的class是由注解解析器自动生成的,主要IRouteRoot,IRouteGroup和IProviderGroup的实现类,比如当我们使用@Route注解某个类时,会自动将这个类的信息注入的到自动生成的上述实现类中。
完成初始化之后会调用afterInit方法,其主要就是注入拦截控制服务(InterceptorServiceImpl)
static void afterInit() {
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
//build方法主要是将path和group封装到Postcard中,可以理解成根据快递号生成一个快递信息表
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方法是根据快递信息表来生成具体的实例,这里的拦截服务控制器实际是InterceptorServiceImpl对象,后面分析具体路由的实现时再看其具体的代码实现。整个初始化流程到注入InterceptorServiceImpl后就基本完成了
如何实现路由功能:
这里以一个具体的使用场景来看下路由的具体实现,我们实现了一个微博分享的服务,并使用@Route标注该服务。如下:
@Route(path = "/service/WBShareService")
public class WBShareServiceImp implements IShareService extends IProvider{
@Override
public void doShareImage(String text, String title, String path, boolean onlyClient) {
}
}
然后客户端需要分享图片到微博时的使用代码如下:
Object obj = ARouter.getInstance().build("/service/WBShareService").navigation();
if (obj instanceof IShareService) {
((IShareService)obj).doShareImage("", "", "", false);
}
前面我们分析过ARouter的build方法了,其就是根据path生成一个Postcard对象,这里我们接着分析navigation方法,postcard对象的navigation方法最终都是委托给_ARouter的navigation方法来处理。
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//到物流中心完成postcard的信息填充,因为最初生成的postcard对象只包含path信息,不包含其他有效信息,比如路由类型,携带的参数等
LogisticsCenter.completion(postcard);
//如果不是绿色通道,则通过拦截控制器依次调用不同的拦截器处理信息(类似与一个包裹在检查通道了经过多个扫描检查)
if (!postcard.isGreenChannel()) {
//每个拦截器的拦截方法调用都是在子线程中执行的
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) {
//只要有一个拦截器拦截该包裹,则回调onInterrupt方法宣告本次路由被终止
callback.onInterrupt(postcard);
}
}
});
} else {
//绿色通道则直接调用_navigation方法进行具体的导航
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
可以看出navigation方法主要做了如下事情:
- 根据只包含path(理解成只有快递单号的快递信息表)的postcard去物流中心查找具体的路由信息(由编译时生成,在init时注入),完成后续步骤需要的数据填充。
- 如果不是绿色通道,则将postcard交予拦截控制器,委托各个拦截器在子线程执行检查是否拦截。
- 如果未拦截,则执行具体的导航功能
这里先看下LogisticsCenter是怎么去填充信息到postcard中:
public synchronized static void completion(Postcard postcard) {
//去数据仓库获取路由信息,该信息在初始化ARouter时已经注入
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
//如果没有路由信息,则尝试去数据仓库查找
} else {
//找到路由信息后,则将配置的路由信息填充到Postcard对象中
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.
//这里主要是完成参数的填充
}
//针对不同的路由类型进行处理
switch (routeMeta.getType()) {
case PROVIDER:
//如果是服务提供者,则尝试获取其具体实例,如果没有,则根据路由信息构造一个实例,初始化并存储到数据仓库,
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);
//服务提供者被设置成绿色渠道,不用接受拦截检查
postcard.greenChannel();
break;
case FRAGMENT:
//fragment也不用拦截检查
postcard.greenChannel();
default:
break;
}
}
}
信息填充完之后,看一下具体的路由实现:
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;
}
从上述代码我们可以看出,不同类型的路由其导航的方式也不一样
- 如果是Activity类型,则将数据填充到intent中之后,调用ActivityCompat的startActivity或者startActivityForResult方法启动activity。
- 如果是PROVIDER类型,则直接返回其服务提供者
- 如果是BOARDCAST || CONTENT_PROVIDER || FRAGMENT,则创建其需要的实体,并填充数据,再返回该实体对象
网友评论