ARouter解析笔记

ARouter的结构

   ARouter主要由三部分组成,包含注解模块、编译期生成相关代码模块以及主要的API调用模块。

  • arouter-annotation: 注解声明和信息存储的模块。

       在开发过程中需要的注解: @Route、 @Interceptor、@Autowired

  • arouter-compiler: 编译期根据注解来分析并生成相关类和文档的模块。

       RouteProcessor、AutowiredProcessor、InterceptorProcessor分别对注解Route、Interceptor、Autowired进行解析并在 com.alibaba.android.arouter.routes 包下生成类 

  • arouter-api: 开发阶段需要调用的API功能的模块

ARouter的初始化流程

  • 调用ARouter的init方法,如下代码:

     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.");
            }
    }
    复制代码

  • 所有工作交给_ARouter这个类去完成,_ARouter内部调用了LogisticsCenter.init()方法。

    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
            mContext = context;
            executor = tpe;
            .....
            Set<String> routerMap;
    
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                //根据com.alibaba.android.arouter.routes包名扫描所有的className
                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>()));
            }
    
            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                  //加载IRouteRoot
                  ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
            //加载IInterceptor              ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                  //加载IProvider
            ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);            }
            }
          }
              .......
          } catch (Exception e) {
              throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
          }
        }复制代码

      Arouter通过 com.alibaba.android.arouter.routes 包名扫描所有对应的ClassName,在              Warehouse中缓存起来。

  • 完成init之后,如果成功则调用_ARouter.afterInit()方法,如下代码:

    static void afterInit() {
        // 初始化拦截器
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }复制代码

    在加载路由完成后会初始化拦截器。

综上述,ARouter的初始化流程大致分为:

  1. 初始化Context
  2. 扫描所有Router相关的类
  3. 缓存路由信息
  4. 初始化拦截器

ARouter跳转界面流程


ARouter的API调用

ARouter跳转页面,获取Fragment以及调用组件服务都是通过 ARouter.getInstance().build(path).navigation() 来调用

  1. build()方法分析
      内部还是调用的_ARouter.build()方法,如下代码:

protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw 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));
    }
}复制代码

     首先判断是否有重定向URL的服务 PathReplaceService 如果有则使用重定向的路径。然后调用 extractGroup 方法将path解析,path必须以"/"开头,截取第一个"/"和第二个"/"之间的内容做为group名称,构建出 Postcard 。

   2. navigation()方法分析

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        //1.通过postCard的path在缓存中找到路由元数据
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        //2.找不到的情况
        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);
    }
     //3. 如果没有绿色通道需要调用拦截器(一般拦截页面跳转)否则就调用_navigation方法进行后续操作
    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) {
                    callback.onInterrupt(postcard);
                }
            }
        });
    } else {
        return _navigation(context, postcard, requestCode, callback);
    }

    return null;
}复制代码
  • 通过上面代码首先调用 LogisticsCenter.completion(postcard) 去获取路由元数据。代码如下:

public synchronized static void completion(Postcard postcard) {
    //部分代码
    //从缓存中通过path获取对应的元数据
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {    // 如果没有获取到,可能是没有加载或者根本没有
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // 从分组的缓存中根据组来找
        if (null == groupMeta) {//如果根据分组找也没有则表示不存在的路径
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {//存在则通过反射生成实例并调用路由组的方法加载并缓存path和对应的路由元数据
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);
            Warehouse.groupsIndex.remove(postcard.getGroup());
            completion(postcard);   // 重新加载... 不明白为什么要重新加载
        }
    } 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) {   
            Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
            Map<String, Integer> paramsType = routeMeta.getParamsType();

            if (MapUtils.isNotEmpty(paramsType)) {
                for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                    setValue(postcard,
                            params.getValue(),
                            params.getKey(),
                            resultMap.get(params.getKey()));
                }
                postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
            }
            postcard.withString(ARouter.RAW_URI, rawUri.toString());
        }

        switch (routeMeta.getType()) {
            case PROVIDER:  //如果是ARouter中的provider查看有没有这个类型对应的实例如果没有则创建并调用init方法然后缓存
                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;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                postcard.setProvider(instance);
                postcard.greenChannel();    // Provider跳过拦截器
                break;
            case FRAGMENT://
                postcard.greenChannel();    // Fragment跳过拦截器
            default:
                break;
        }
    }
}复制代码

整个过程,如下:

  1. ARouter通过按需加载路由,首先通过path先去Warehouse.routes中找,如果没有则通过group去 Warehouse.groupsIndex 的缓存中找对应的分组并加载该分组所有的path和对应的路由元数据,并将该路由分组从缓存中移除。
  2. 然后将找到的RouteMeta赋值给PostCard。包括 目标的class、路由的类型、优先级等
    Uri rawUri = postcard.getUri();
    if (null != rawUri) {   // 如果有Uri
        //解析Uri
        //比如arouter://com.zh.arouterdemo/me/main/eventbus?id=1000&name='zhangsan'&age=5
        //将?后面的参数解析转换成Map形式
        Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
         //必须指定参数的,例如:id=1000
         //Map<String, Integer> paramType = new HashMap<>();
         //paramType.put("id", TypeKind.INT.ordinal());
        Map<String, Integer> paramsType = routeMeta.getParamsType();
    
        if (MapUtils.isNotEmpty(paramsType)) {
            // Set value by its type, just for params which annotation by @Param
            for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                setValue(postcard,
                        params.getValue(),
                        params.getKey(),
                        resultMap.get(params.getKey()));
            }
            postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
        }
        postcard.withString(ARouter.RAW_URI, rawUri.toString());
    }复制代码

       如果是通过Uri路由ARouter将对uri生成的path进行解析提取path "?" 后面携带的键值对参数并转换成Map格式,然后通过自定义的ParamType来将数据存入Bundle中

    3. 根据type判断如果是Provider,查看有没有这个类型对应的实例如果没有则创建并调用init方法然后缓存。provider和Fragment都跳过拦截器。

  • 如果没有任何阻碍将调用_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:
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });
    
                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则存数据跳转页面,如果是Provider则返回Provider对象,如果是Fragment则创建出Fragment的实例并返回。

ARouter拦截器

拦截器会在路由跳转的时候执行,如果有多个拦截器,将会按照优先级的高低依次执行,优先级通过RouteMeta的priority属性来制定,值越小优先级越高。

首先从初始化开始,代码如下:

static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
public class ARouter$$Group$$arouter implements IRouteGroup {
    public ARouter$$Group$$arouter() {    }

    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/arouter/service/autowired", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", (Map)null, -1, -2147483648));
        atlas.put("/arouter/service/interceptor", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", (Map)null, -1, -2147483648));
    }
}复制代码

 通过 /arouter/service/interceptor 找到的实际上就是 InterceptorServiceImpl.class 然后实例化InterceptorServiceImpl。由于它是Provider属性所以第一次会调用它的init方法

@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) {
                        throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                    }
                }

                interceptorHasInit = true;

                logger.info(TAG, "ARouter interceptors init over.");

                synchronized (interceptorInitLock) {
                    interceptorInitLock.notifyAll();
                }
            }
        }
    });
}复制代码

在init方法中会将 Warehouse.interceptorsIndex 中的所有缓存遍历并生成拦截器的实例并调用它们的init方法,然后将拦截器缓存到 Warehouse.interceptors。

拦截器的流程

在讲路由跳转的时候提到如果不是绿色通道会执行拦截器的方法。如下代码:

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) {
                    callback.onInterrupt(postcard);
                }
            }
        });
 } 复制代码

实际上调用的是 InterceptorServiceImpl 中的 doInterceptions 方法。

 @Override
    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {

            checkInterceptorsInitStatus();

            if (!interceptorHasInit) {
                callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
                return;
            }

            LogisticsCenter.executor.execute(new Runnable() {
                @Override
                public void run() {
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {
                        _excute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        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(new HandlerException(postcard.getTag().toString()));
                        } else {
                            callback.onContinue(postcard);
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }

    private static void _excute(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) {
                    // Last interceptor excute over with no exception.
                    counter.countDown();
                    _excute(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) {
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    counter.cancel();
                }
            });
        }
    }

    private static void checkInterceptorsInitStatus() {
        synchronized (interceptorInitLock) {
            while (!interceptorHasInit) {
                try {
                    interceptorInitLock.wait(10 * 1000);
                } catch (InterruptedException e) {
                    throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
                }
            }
        }
    }复制代码

首先检查初始化的状态,如果初始化没有完成,拦截器将一直等待直到初始化方法完成为止。

然后异步调用_excute 方法并等待方法调用完成,_excute 内部会根据index 依次调用 Warehouse.interceptors 中的拦截器的 process 方法, onContinue 代表拦截器放行本次拦截,放行后将继续执行下一个拦截器,onInterrupt 表示拦截器拦截本次跳转,如果当前操作被拦截,后续拦截器将不再执行。

当拦截器的操作执行完后,会根据条件判断:

if (interceptorCounter.getCount() > 0) {//如果还有未完成拦截器表示拦截器超时了将进行拦截
    callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) {//如果Tag不为空表示拦截
    callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else {//否则放行
    callback.onContinue(postcard);
}复制代码

如果放行将继续调用_navigation()方法,否则回调CallBack中的onInterrupt方法。



转载于:https://juejin.im/post/5cef4956e51d45775d516f19

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值