ARouter源码分析(二)—— 拦截器源码分析

痛定思痛。 2022-04-24 13:06 413阅读 0赞

Arouter源码分析系列文章,请访问https://github.com/AlexMahao/ARouter

在分析路由跳转时,最终的跳转会判断是否是绿色通道,如果不是,将会走拦截器相关的逻辑。

  1. // 如果不是绿色通多,拦截器做拦截
  2. if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
  3. interceptorService.doInterceptions(postcard, new InterceptorCallback() {
  4. /** * Continue process * * @param postcard route meta */
  5. @Override
  6. public void onContinue(Postcard postcard) {
  7. _navigation(context, postcard, requestCode, callback);
  8. }
  9. /** * Interrupt process, pipeline will be destory when this method called. * * @param exception Reson of interrupt. */
  10. @Override
  11. public void onInterrupt(Throwable exception) {
  12. if (null != callback) {
  13. callback.onInterrupt(postcard);
  14. }
  15. logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
  16. }
  17. });
  18. } else {
  19. return _navigation(context, postcard, requestCode, callback);
  20. }

那么拦截器到底是怎么实现的呢?

逻辑分析

官方demo如下:

  1. @Interceptor(priority = 7)
  2. public class Test1Interceptor implements IInterceptor { }
  • 编译时期生成拦截器辅助类
  • 初始化时期加载拦截器辅助类,并初始化拦截器服务。
  • 跳转时期进行拦截器执行

编译时期生成辅助工具类

编译时期通过apt@Interceptor做处理,生成拦截器辅助类。处理类为InterceptorProcessor。逻辑比较简单,看一下生成的辅助类代码:

  1. public class ARouter$$Interceptors$$app implements IInterceptorGroup {
  2. @Override
  3. public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
  4. interceptors.put(7, Test1Interceptor.class);
  5. }
  6. }

该辅助类就是将拦截器的类存入到一个集合中去。其中key是声明的拦截器的优先级。

初始化时期加载拦截器

在之前分析路由跳转时,加载路由表到Warehouse中存储是通过LogisticsCenter.init()方法,该方法中获取com.alibaba.android.arouter.routes下面的所有类,其中包含了Interceptors的辅助类。在该方法中加载辅助类并将Interceptor存储到Warehouse中。

  1. public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
  2. try {
  3. // ....
  4. if (registerByPlugin) {
  5. logger.info(TAG, "Load router map by arouter-auto-register plugin.");
  6. } else {
  7. Set<String> routerMap;
  8. for (String className : routerMap) {
  9. if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
  10. // This one of root elements, load root.
  11. ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
  12. } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
  13. // Load interceptorMeta
  14. // ==== 加载插件代码 ===
  15. ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
  16. } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
  17. // Load providerIndex
  18. ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
  19. }
  20. }
  21. }
  22. } catch (Exception e) {
  23. throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
  24. }
  25. }

这只是加载了插件,同时在ARouter.init()方法中,有如下逻辑

  1. public static void init(Application application) {
  2. if (!hasInit) {
  3. logger = _ARouter.logger;
  4. _ARouter.logger.info(Consts.TAG, "ARouter init start.");
  5. // 初始化操作
  6. hasInit = _ARouter.init(application);
  7. if (hasInit) {
  8. // 初始化拦截器
  9. _ARouter.afterInit();
  10. }
  11. _ARouter.logger.info(Consts.TAG, "ARouter init over.");
  12. }
  13. }

其中_Arouter.init()是对辅助类进行加载。然后调用了_ARouter.afterInit()进行加载插件。

  1. static void afterInit() {
  2. // Trigger interceptor init, use byName.
  3. interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
  4. }

该方法获取interceptorService的实例,该实例其实也是一个路由地址。ARouter提供一种加载实例的方式,就是Provider,通过该机制,可以@Router注解的方式,获取对象实例。这个后面文章分析。

而对于InterceptorService,最终实例化的对象就是InterceptorServiceImpl类。

并且实例化对象的时候,会调用InterceptorServiceImpl.init()方法。

  1. @Override
  2. public void init(final Context context) {
  3. // 启动一个子线程
  4. LogisticsCenter.executor.execute(new Runnable() {
  5. @Override
  6. public void run() {
  7. // 如果interceptors如果不为null,则进行初始化
  8. if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
  9. for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
  10. Class<? extends IInterceptor> interceptorClass = entry.getValue();
  11. try {
  12. // 构造Interceptor实例
  13. IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
  14. // 调用Interceptor的init方法
  15. iInterceptor.init(context);
  16. // 保存Interceptor对象
  17. Warehouse.interceptors.add(iInterceptor);
  18. } catch (Exception ex) {
  19. throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
  20. }
  21. }
  22. // 初始化完成标识
  23. interceptorHasInit = true;
  24. logger.info(TAG, "ARouter interceptors init over.");
  25. synchronized (interceptorInitLock) {
  26. interceptorInitLock.notifyAll();
  27. }
  28. }
  29. }
  30. });
  31. }

此处仅仅是初始化一个InterceptorServiceImpl对象并保存。

跳转时期,进行拦截

路由在跳转是时期,判断是否是绿色通道不然拦截并调用如下代码:

  1. interceptorService.doInterceptions(postcard, new InterceptorCallback() {
  2. /** * Continue process * * @param postcard route meta */
  3. @Override
  4. public void onContinue(Postcard postcard) {
  5. _navigation(context, postcard, requestCode, callback);
  6. }
  7. /** * Interrupt process, pipeline will be destory when this method called. * * @param exception Reson of interrupt. */
  8. @Override
  9. public void onInterrupt(Throwable exception) {
  10. if (null != callback) {
  11. callback.onInterrupt(postcard);
  12. }
  13. logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
  14. }
  15. });

实质就是调用 InterceptorServiceImpl.doInterceptions().

  1. @Override
  2. public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
  3. if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
  4. // 判断是否初始化完成,该方法是一个循环,除非打断,不然不会结束
  5. checkInterceptorsInitStatus();
  6. // 如果初始化失败,则中断路由跳转
  7. if (!interceptorHasInit) {
  8. callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
  9. return;
  10. }
  11. LogisticsCenter.executor.execute(new Runnable() {
  12. @Override
  13. public void run() {
  14. // 线程同步工具类
  15. CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
  16. try {
  17. // 执行所有拦截器
  18. _excute(0, interceptorCounter, postcard);
  19. // 阻塞线程,等待拦截器执行或者超时
  20. interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
  21. // 如果getCount>0,说明拦截器没有执行完毕就超时了
  22. if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
  23. callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
  24. } else if (null != postcard.getTag()) { // Maybe some exception in the tag.
  25. // 某个拦截器发起的拦截操作
  26. callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
  27. } else {
  28. // 继续进行路由跳转
  29. callback.onContinue(postcard);
  30. }
  31. } catch (Exception e) {
  32. callback.onInterrupt(e);
  33. }
  34. }
  35. });
  36. } else {
  37. // 如果没有拦截器,直接回调成功,继续路由跳转
  38. callback.onContinue(postcard);
  39. }
  40. }

判断是否有拦截器,如果没有,则直接回调路由跳转。否则进行拦截器拦截操作。主要流程如下:

检查拦截器加载是否完成。因为拦截器的加载和初始化是放在子线程进行,并且是在路由初始化时期。所以需要检查状态。如果拦截器初始化失败,则直接中断路由跳转。

在子线中开始准备拦截器遍历,在这里用到了CancelableCountDownLatch,该类继承CountDownLatch,主要用于线程间同步。

然后执行拦截器遍历_excute()。该方法主要是拦截器的遍历和执行。

  1. private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
  2. if (index < Warehouse.interceptors.size()) {
  3. // 获取拦截器
  4. IInterceptor iInterceptor = Warehouse.interceptors.get(index);
  5. // 调用拦截器 process 方法
  6. iInterceptor.process(postcard, new InterceptorCallback() {
  7. @Override
  8. public void onContinue(Postcard postcard) {
  9. // Last interceptor excute over with no exception.
  10. // 计数,用于线程同步
  11. counter.countDown();
  12. // 下一个拦截
  13. _excute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
  14. }
  15. @Override
  16. public void onInterrupt(Throwable exception) {
  17. // Last interceptor excute over with fatal exception.
  18. // 中断标记
  19. postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); // save the exception message for backup.
  20. // 结束线程同步,置计数器为0
  21. counter.cancel();
  22. }
  23. });
  24. }
  25. }

拦截器的遍历有一个关键点,执行下一个拦截器的前提是前一个拦截器调用了InterceptorCallback.onContinue()方法。如果没有回调,那么久不会执行下一个拦截器。

继续回到doInterceptions()方法。

  1. // 阻塞线程,等待拦截器执行或者超时
  2. interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);

由于上面的问题,这一行代码就很关键。万一拦截器没有回调并开始下一个拦截,那么该行代码将会阻塞,直到所有的拦截器被执行或者是超时。通过代码看,默认超时时间是300s。

再往下就是根据拦截器的结果进行不同逻辑的分发了。

发表评论

表情:
评论列表 (有 0 条评论,413人围观)

还没有评论,来说两句吧...

相关阅读