博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ARouter解析笔记
阅读量:6229 次
发布时间:2019-06-21

本文共 18338 字,大约阅读时间需要 61 分钟。

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
    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
    ())); } 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
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
resultMap = TextUtils.splitQueryParameters(rawUri); Map
paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { for (Map.Entry
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
providerMeta = (Class
) 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
    resultMap = TextUtils.splitQueryParameters(rawUri); //必须指定参数的,例如:id=1000 //Map
    paramType = new HashMap<>(); //paramType.put("id", TypeKind.INT.ordinal()); Map
    paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { // Set value by its type, just for params which annotation by @Param for (Map.Entry
    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
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方法

@Overridepublic void init(final Context context) {    LogisticsCenter.executor.execute(new Runnable() {        @Override        public void run() {            if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {                for (Map.Entry
> entry : Warehouse.interceptorsIndex.entrySet()) { Class
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

你可能感兴趣的文章
一个看起来呆萌的核物理学霸为何两次收到BlackHat的邀请
查看>>
国际篮联三人篮球亚洲杯连续三年落户长沙
查看>>
长沙开通首条直飞缅甸曼德勒国际航线
查看>>
探访新疆喀什老城:从连片危房到5A景区
查看>>
重庆市交通行政执法总队发布春运“避堵”线路图
查看>>
北京所有社区卫生服务中心今年将全部“先诊疗、后结算”
查看>>
QNAP(威联通)联合创始人郭博达先生一行到访
查看>>
浏览器中唤起native app || 跳转到应用商城下载(一)
查看>>
springboot(十七):使用Spring Boot上传文件
查看>>
Vue中你不知道但却很实用的黑科技
查看>>
阿里云消息中间件(MQ)探秘
查看>>
基础排序算法详解与优化
查看>>
与我一起学正则
查看>>
练就Java24章真经—你所不知道的工厂方法
查看>>
手游中实时音视频的开发经验与实现技巧
查看>>
saiku+kettle整合(十)缓存控制
查看>>
微信支付,支付宝支付,银联支付——三大支付总结
查看>>
Android Sunflower 带您玩转 Jetpack
查看>>
Android 应用兼容性最佳实践 | 中文教学视频
查看>>
SQL语句笔记
查看>>