前言
在上一篇文章ARouter解析(上) - 编译期原理中,我们了解了ARouter在编译期利用了注解处理器与JavaPoet,对我们声明的Route、拦截器等注解进行了预加载。
讲解的话主要围绕源码+注释展开解说,尽量在每章节的总结处贴一下时序图,保持对流程的理解。
本篇的话依然是带着问题来探索:
- 编译期解析出来的数据到底怎么使用?
- 路由跳转的全过程是怎么样的?
1. 初始化过程
首先我们来了解一下ARouter初始化的过程,了解其初始化到底做了什么?
ARouter.init(this)
ARouter的初始化需要执行以上代码,那么我们就顺着源码流程一探究竟
public final class ARouter {
private ARouter() {
}
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
//实际上是调用了_ARouter的init方法
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
}
}
public static ARouter getInstance() {
if (instance == null) {
synchronized (ARouter.class) {
if (instance == null) {
instance = new ARouter();
}
}
}
return instance;
}
//...some method
}
我们可以看出来,ARouter本身为了保持简洁,具体的流程是交由了_ARouter去执行,ARouter本身不包含具体逻辑,接着我们深入_ARouter的init()中看看
final class _ARouter {
private _ARouter() {
}
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
}
我们发现_ARouter最后是调用了LogisticsCenter的init方法进行了初始化,这小子藏得有点深,我们继续跟,
public class LogisticsCenter {
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
//这里判断我们是否使用了ARouter的插件,我们暂时先不讲解这部分,默认不使用,此时进入else逻辑
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
//这里判断我们的路由文件是否有缓存过与改变,如果缓存过并且没改变的话会读取之前的缓存,否则就重新扫描一次Dex文件,获取我们编译期生成的文件
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
//如果是首次读取,则走该逻辑
//调用getFileNameByPackageName方法扫描Dex文件,找出编译期生成的文件
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);
} else {
//之前读取过,并且生成了缓存,则走该逻辑
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//遍历刚刚扫描出来的文件,一共是三种类型,Root、拦截器、服务三种文件
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
/*
找出编译期生成的Root文件,并且通过反射初始化,调用其loadInto方法,将路由组文件的Class路径保存到Warehouse类中的groupsIndex中,
需要注意的是,此时路由组文件并未初始化
*/
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
/*
找出其编译期生成的Interceptor文件,并且将其加载到Warehouse类中的interceptorsIndex中,
需要注意的是,此时拦截器并未初始化
*/
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
/*
找出其编译期生成的服务文件,并且将其加载到Warehouse类中的providersIndex中,
需要注意的是,此时服务并未初始化
*/
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
} catch (Exception e) {
//...
}
}
//该方法用于扫描Dex文件中ARouter在编译期生成的文件
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
//获取全部的Dex文件路径
List<String> paths = getSourcePaths(context);
//声明了一个锁,保证新线程扫描完毕后才返回
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
//开始循环匹配Dex文件下的Class文件
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
//加载Dex文件
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
//扫描Dex文件
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
//如果符合ARouter编译期生成的Java文件,则将该文件保存到Set集合中
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
//该锁会等待线程的遍历完成
parserCtl.await();
//返回编译期文件的全类名
return classNames;
}
}
我们可以通过阅读注释获知,在LogisticsCenter的init方法中,他主要的初始化工作是扫描全部Dex文件,获得Root、拦截器、服务三个在编译期中生成的类文件,最后保存到Warehouse中。
接下来我们看看Warehouse的样子是什么,他有什么作用?
class Warehouse {
//缓存编译期保存在Root文件中的路由组class文件(此时未初始化)
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
//该Map缓存的是具体的一个个已解析的路由信息
static Map<String, RouteMeta> routes = new HashMap<>();
//缓存已经使用过的IProvider对象
static Map<Class, IProvider> providers = new HashMap<>();
//加载编译期解析的Provider(此时未初始化)
static Map<String, RouteMeta> providersIndex = new HashMap<>();
//加载编译期解析的拦截器(此时未初始化)
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
//缓存已经使用过的拦截器
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
我们可以从Warehouse源码得知,Warehouse只是一个仓库一样的存在,负责预存编译期解析的数据,但是这些数据未进行初始化,初始化的数据会保存到不含index名字的Map中。
到了这里我们把刚刚出现过的类拎出来做一个总结:
- ARouter
ARouter只是一个简单的包装类,只负责向开发者暴露其方法,实际由_ARouter进行代理。
- _ARouter
ARouter的代理类,具体的逻辑都会声明在该类中,这个做法的好处就是使得ARouter保持简洁。
- LogisticsCenter
ARouter的设计比较有意思,参考了物流的做法,该类其实就是一个物流中心,主要有两个职责,一个是负责解包编译期解析出来的数据,缓存到Warehouse中,第二就是当我们使用ARouter进行路由跳转时,它会从Warehouse中获取对应的路由数据,并且对其进行初始化,组装到Postcard类中(Postcard后面会进行说明)。所以该类主要是围绕对Warehouse的路由数据进行存取而存在的。
- Warehouse
Warehouse如命名一般,它是一个仓库,负责保存编译期的数据与运行期需要执行的路由数据。
ok,那么源码分析到这里,我们知道的ARouterinit过程,只是将编译期的数据暂存到Warehouse中,那么这些数据又是什么时候拿出来使用的呢?我们接着看!
2. 路由跳转全过程
首先我们看一下时序图,对流程有一个大概的认知
无论是路由跳转还是获得服务,我们都是通过ARouter.getInstance().build("path")获取Postcard对象,最后再通过navigation方法进行路由跳转或者服务对象获得,接下来我们就要将该过程完全剖析,再就是弄清楚拦截器是什么时候生效的,全局降级策略又是在什么时候执行的?这些问题我们统统会再该流程中搞清楚!
接下来我们先看一个路由跳转和服务获得都是怎么做的?
//1.路由跳转
ARouter.getInstance().build("/main/mainActivity").withString("obj1", "obj1").navigation();
//2.获得Provider
HelloService helloService = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
通过上面这两个示例,我们可以分出ARouter路由跳转一般分两个步骤,一个是build,另一个是navigation,那接下来我们就围绕这两个进行解析。
2.1 ARouter是如何构建路由信息和跳转的?
ARouter.getInstance().build("/main/mainActivity")
我们一般通过ARouter的实例,调用其build方法,获取一个Postcard对象,而这个Postcard对象是什么呢?我们先贴出其源码看看
public final class Postcard extends RouteMeta {
// Base
private Uri uri; //统一资源标识符
private Object tag; //报签
private Bundle mBundle; //传递参数
private int flags = 0; //如果是Activity,则用于决定 启动模式
private int timeout = 300; //超时时间
private IProvider provider; //具体服务对象
private boolean greenChannel; //是否绿色通道(是绿色通道的话,不会被拦截器拦截)
private Bundle optionsCompat; //共享元素动画
private int enterAnim = -1; //入场动画
private int exitAnim = -1; //退场动画
}
public class RouteMeta {
private RouteType type; // 类型,比如Activity、Fragment、Provider等
private Class<?> destination; // 路由目的所对应的类型,比如XXXActivity
private String path; // 路径
private String group; // 路由所在的组
private int priority = -1; // 优先级
private int extra; // 32位的参数
private Map<String, Integer> paramsType; // 传递参数
private String name; //名称
}
Postcard就是明信片,它继承至RouteMeta,它的作用就是知道该次路由的目的地与一些路由的信息,同时在初始化的时候,我们知道ARouter有物流中心,有仓库,那么明信片就是决定这次路由的目的地与携带的一些额外信息。
在概念上理解Postcard之后,我们接着看ARouter是怎么初始化它的,我们从ARouter的build方法开始分析
//果不其然,ARouter是调用了_ARouter的方法
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
我们继续跟
final class _ARouter {
//1.该方法会给予一次路径处理的timming,我们可以实现PathReplaceService进行干预,接着会继续调用build方法
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
//ARouter在这里提供了第一次路由干预手段,我们可以在ARouter中实现PathReplaceService,对路径进行处理
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
//调用extractGroup方法获取路径在的Group Name
return build(path, extractGroup(path), true);
}
}
}
//2.一般在这里就不会触发第二次路径处理,直接new了一个PostCard出来,把路径和组名传递进去
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
那build也没做什么事情呀,但是PostCard目前只是初始化了path和group,我们刚刚看到的其他信息都没进行初始化,那么一切秘密都藏在Postcard的navigation流程中。
public final class Postcard extends RouteMeta {
public Object navigation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
}
最后我们发现Postcard会将自己传递给ARouter的navigation方法,真是跳来跳去的。没事,我们继续跟!狠狠跟!
final class _ARouter {
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//ARouter在这里提供第二次干预路由的timming,我们可以PretreatmentService来对Postcard进行修改
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
//为Postcard设置context,这里有可能是application,如果是application在跳转Activity的时候,会自动标记NEW_TASK
postcard.setContext(null == context ? mContext : context);
try {
//之前构建的postCard信息尚未完整,所以需要通过物流中心在仓库中寻找该Postcard的信息,并且进行完善,那么接着我们看看物流中心是怎么完善postCard的信息的
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
//...暂时省略后续流程,我们先关注 LogisticsCenter.completion方法
}
}
我们先看看LogisticsCenter的completion方法
public synchronized static void completion(Postcard postcard) {
/*
1. ARouter会先从Warehouse中的routes获得路由信息,但是之前说了,Warehouse,routers只在init中初始化了路由组class,
但是还没有初始化路由组信息所以在第一次的时候这里是不可能获取到任何对象,所以这里会进入if逻辑里
*/
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
try {
//2.该方法正式初始化路由组里面的路由信息,我们可以往下滑,先看addRouteGroupDynamic方法具体实现。
addRouteGroupDynamic(postcard.getGroup(), null);
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
//4.在初始化路由组后,我们重新加载一下,因为此时Warehouse的routesMap中会含有我们目标路由的RouteMeta数据,此时会进入
completion(postcard); // Reload
} else {
//5.将仓库之前保存的信息取出,并且保存到postcard中
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
//...暂时忽略uri相关逻辑
switch (routeMeta.getType()) {
case PROVIDER:
//6.如果该路由最后获取的是服务,
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
//7.优先从缓存中获取IProvider对象,如果没有则进行初始化,并且保存到Warehouse的providers中。
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
//将对象保存到postCard中,并且设置为绿色通道
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
//如果是Fragment则设置为绿色通道,跳过拦截器
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
//至此 postCard信息完善完毕
}
}
//3.初始化路由组
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//如果groupsIndex(未初始化的路由组缓存)中含有该GroupName的数据,则取出
if (Warehouse.groupsIndex.containsKey(groupName)){
/*
对其class对象进行反射构建,然后调用其loadInto方法;我们回顾一下该对象长什么样子?
public class ARouter$$Group$$main implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/main/mainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/main/mainactivity", "main", new java.util.HashMap<String, Integer>(){{put("testObj", 10); put("spec", 8); }}, -1, -2147483648));
atlas.put("/main/mainFragment", RouteMeta.build(RouteType.FRAGMENT, MainFragment.class, "/main/mainfragment", "main", null, -1, -2147483648));
atlas.put("/main/mainServiceImpl", RouteMeta.build(RouteType.PROVIDER, MainServiceImpl.class, "/main/mainserviceimpl", "main", null, -1, -2147483648));
}
}
所以在调用loadInto后,就会将编译期中解析的Route注解的相关类保存到Warehouse的routes Map中,正式的初始化与加载路由组文件。
*/
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
//初始化后删除该路由组,让后续的路由组初始化效率提高。
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
此时Postcard通过物流中心在仓库中获取了路由原本完整的信息,Postcard完善完毕,接下来就是路由跳转关键的逻辑,我们回到ARouter的navifation方法中,讲完刚刚中断了的逻辑
final class _ARouter {
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//...
try {
//刚刚讲到在这里,Postcard向LogisticsCenter获得了完整的postCard信息
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
//如果报错了,有回调的情况下会回调onLost,告诉调用者路由失败,如果没有回调,则会通知全局降级策略服务,处理这次路由错误的事件
if (null != callback) {
callback.onLost(postcard);
} else {
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//通知回调,完善明信片的信息
if (null != callback) {
callback.onFound(postcard);
}
//如果该明信片不是绿色通道,则会遍历所有拦截器,按照优先级处理该postCard是否需要拦截
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(postcard, requestCode, callback);
}
//如果拦截成功最后会通知回调
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else {
return _navigation(postcard, requestCode, callback);
}
}
}
最后会调用_navigation方法,我们接着跟!
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
//判断postcard的类型
switch (postcard.getType()) {
case ACTIVITY:
//如果是Activity,则构建Intent对象,并且将Activity的class对象传进去
final Intent intent = new Intent(currentContext, postcard.getDestination());
//传递参数
intent.putExtras(postcard.getExtras());
//设置Intent的Flag
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
}
//如果context不是Activity,则设置NewTaskFlag
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
//设置对应的Action
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
//通过Router的startActivity进行跳转
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
//返回服务对象
return postcard.getProvider();
case BOARDCAST://广播
case CONTENT_PROVIDER://内容观察者
case FRAGMENT://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());
}
//这里会返回广播、内容观察者与Fragment,如果是Fragment的话会将页面参数塞进去。
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;
}
至此,ARouter整个路由跳转的流程基本上到这里就结束了,因为是通过源码来分析整个流程,所以整体可能会云里雾里,但是我们要看回一开始的时序图,按照时序图的顺序我们再熟读源码,这个时候才会对ARouter运行时的流程有一个更加清晰的印象。
3.拦截器是什么时候初始化的?
我们在上面的流程中只看到了以下代码
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
//如果不需要拦截最后会调用该方法
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
//如果拦截成功最后会通知回调
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
但是拦截器具体的初始化在哪里?我们是没看到的,我们定义了这么多个拦截器,又是怎么遍历调用的?答案在ARouter初始化成功后的afterInit方法中
class ARouter{
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.");
}
}
}
class _ARouter{
static void afterInit() {
// 初始化interceptorService服务
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
}
接下来我们看看InterceptorService的源码
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
//...省略部分代码
//InterceptorServiceImpl也是继承自Provider,所以在初始化时会调用init方法,在方法中会初始化编译期预加载的所有拦截器,并且保存到Warehouse的interceptors中。
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
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) {
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
}
然后在ARouter中,调用拦截器服务的doInterceptions方法
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
//拦截器会从该方法开始
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
//新建一个线程锁
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
//由第0个拦截器开始执行,并且进行递归执行全部拦截器,直到递归完为止
_execute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
//如果还有拦截器未执行完或者设置了Tag,则判断途中拦截器生效了
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((Throwable) postcard.getTag());
} else {
//否则全部拦截器已执行完毕,没有被拦截
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
//开始倒数Counter,直到没有为止
counter.countDown();
//递归执行下一个拦截器
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
//如果触发了拦截,则设置Tag,并且释放锁
postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.
counter.cancel();
}
});
}
}
}
至此,拦截器的源码分析完毕。
讲到这里,ARouter运行时的核心流程已经讲完,这篇主要是按照源码流程来讲,所以会有点枯燥,希望同学们能提起耐性反复阅读,最后理解ARouter的运行时原理。感谢大家~
我的其它系列文章: