ARouter源码解析

作者: toothpickTina | 来源:发表于2018-05-18 16:01 被阅读72次

    如果应用项目要做模块化, 必然绕不开为了模块间跳转解耦而做的处理, 我们都知道一般都会采用路由模式, 本篇主要解析阿里的开源路由框架ARouter, 源码地址可见Github,本篇分析版本为api1.3.1

    初始化

    感谢源码自带Demo, 我们直接从他的整个使用流程开始看起.忽略掉Log模式和Debug模式的开启, 首先我们需要初始化ARouter

    ARouter.init(getApplication());
    

    它内部实际调用的是LogisticsCenter.init(Context context, ThreadPoolExecutor tpe)

    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
            mContext = context;
            executor = tpe;
    
            try {
                long startInit = System.currentTimeMillis();
                //billy.qi modified at 2017-12-06
                //load by plugin first
                loadRouterMap();
                if (registerByPlugin) {
                    logger.info(TAG, "Load router map by arouter-auto-register plugin.");
                } else {
                    Set<String> routerMap;
    
                    // It will rebuild router map every times when debuggable.
                    if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                        logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                        // These class was generated by arouter-compiler.
                        // 通过arouter-compiler生成
                        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();
                        }
    
                        // 当router map更新的时候保存新的版本号
                        PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
                    } 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>()));
                    }
    
                    logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
                    startInit = System.currentTimeMillis();
    
                    for (String className : routerMap) {
                        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                            // This one of root elements, load 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);
                        }
                    }
                }
    
                logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
    
                if (Warehouse.groupsIndex.size() == 0) {
                    logger.error(TAG, "No mapping files were found, check your configuration please!");
                }
    
                if (ARouter.debuggable()) {
                    logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
                }
            } catch (Exception e) {
                throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
            }
        }
    

    这里loadRouterMap()方法就是将是否通过插件注册的tag初始化为false, 我们主要看正常流程下的注册, 所以关注点在else的代码块中.对于routerMap从上下文我们可以看出, 他是APT自动生成的类名集合.

    Debug模式下, 当每次更新路由缓存的版本的时候, 都会从指定包名com.alibaba.android.arouter.routes收集所有的className, 如果不为空 则更新磁盘缓存.

    而非Debug模式下, 不做收集, 直接获取磁盘缓存, 如果没有则new一个空集合.

    然后通过反射新建对应类实例并调用load方法.我们可以看下debug模式下, APT生成类文件下的load函数具体执行了什么

    public class ARouter$$Group$$arouter implements IRouteGroup {
      @Override
      public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/arouter/service/autowired", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", null, -1, -2147483648));
        atlas.put("/arouter/service/interceptor", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", null, -1, -2147483648));
      }
    }
    
    public class ARouter$$Providers$$arouterapi implements IProviderGroup {
      @Override
      public void loadInto(Map<String, RouteMeta> providers) {
        providers.put("com.alibaba.android.arouter.facade.service.AutowiredService", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", null, -1, -2147483648));
        providers.put("com.alibaba.android.arouter.facade.service.InterceptorService", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", null, -1, -2147483648));
      }
    }
    
    public class ARouter$$Root$$arouterapi implements IRouteRoot {
      @Override
      public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        routes.put("arouter", ARouter$$Group$$arouter.class);
      }
    }
    

    可以看出所有loadInto()方法执行的都是将注解的信息通过Map强引用做内存缓存管理.这里Warehouse.groupsIndex管理的是组的路由生成文件的内存, Warehouse.interceptorsIndex拦截器的索引管理, Warehouse.providersIndex服务(不是四大组件, 而是实现了继承IProvider接口的对象)索引管理.

    跳转

    我们先看下对应的API

    ARouter.getInstance()
           .build("/test/activity2")
           .navigation();
    

    首先需要build一个Postcard对象, 他包含了我们传递的跳转信息.

    protected Postcard build(String path) {
            if (TextUtils.isEmpty(path)) {
                throw new HandlerException(Consts.TAG + "Parameter is invalid!");
            } else {
              // 判断是否重写跳转URL, 没有就使用原来的path构建Postcard
                PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
                if (null != pService) {
                    path = pService.forString(path);
                }
                return build(path, extractGroup(path));
            }
        }
    
    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);
            }
        }
    

    然后通过postcard.navigation()做跳转工作, 在这个方法内首先会针对我们记载路由信息的postcard通过调用LogisticsCenter.completion(postcard);进行信息补充完善

    public synchronized static void completion(Postcard postcard) {
            if (null == postcard) {
                throw new NoRouteFoundException(TAG + "No postcard!");
            }
    
            // 通过path获取路由基础信息
            RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
            // 如果没有获取到路由基础信息
            if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
                // 则重新通过反射进行加载
                Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
                if (null == groupMeta) {
                    throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
                } else {
                    // Load route and cache it into memory, then delete from metas.
                    try {
                        if (ARouter.debuggable()) {
                            logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                        }
    
                        IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                        iGroupInstance.loadInto(Warehouse.routes);
                        Warehouse.groupsIndex.remove(postcard.getGroup());
    
                        if (ARouter.debuggable()) {
                            logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                        }
                    } catch (Exception e) {
                        throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                    }
    
                    // 递归进行路由的完善工作
                    completion(postcard);   // Reload
                }
            } 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.
                    Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                    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()));
                        }
    
                        // Save params name which need auto inject.
                        postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                    }
    
                    // Save raw uri
                    postcard.withString(ARouter.RAW_URI, rawUri.toString());
                }
    
                switch (routeMeta.getType()) {
                    case PROVIDER:  // if the route is provider, should find its instance
                        // Its provider, so it must implement IProvider
                        Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                        IProvider instance = Warehouse.providers.get(providerMeta);
                        switch (routeMeta.getType()) {
                    case PROVIDER:  // if the route is provider, should find its instance
                        // Its provider, so it must implement IProvider
                        // 如果路由目标是一个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;
                            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);
                        // provider不会触发拦截器
                        postcard.greenChannel();    // Provider should skip all of interceptors
                        break;
                    case FRAGMENT:
                        // fragment不会触发拦截器
                        postcard.greenChannel();    // Fragment needn't interceptors
                    default:
                        break;
                }
            }
        }
    

    然后再回来看下路由跳转做的事情.

    /**
         * Use router navigation.
         *
         * @param context     
         * @param postcard    路由信息
         * @param requestCode RequestCode, 默认为-1
         * @param callback    申明的跳转回调
         */
    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
            try {
              // postcard信息完善
                LogisticsCenter.completion(postcard);
            } catch (NoRouteFoundException ex) {
                logger.warning(Consts.TAG, ex.getMessage());
    
                if (debuggable()) { // Show friendly tips for user.
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
                // 没有找到对应路由信息的回调通知, 如果没有回调, 会通过DegradeService服务进行通知, 但是要根据他发出的进行处理, 需要我们实现DegradeService的接口
                if (null != callback) {
                    callback.onLost(postcard);
                } else {    // No callback for this invoke, then we use the global degrade service.
                    DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                    if (null != degradeService) {
                        degradeService.onLost(context, postcard);
                    }
                }
    
                return null;
            }
    
            // 回调通知onFound
            if (null != callback) {
                callback.onFound(postcard);
            }
            // 当需要处理拦截器内容的时候
            if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
                // 拦截器处理
                interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                    /**
                     * Continue process
                     *
                     * @param postcard route meta
                     */
                    @Override
                    public void onContinue(Postcard postcard) {
                      // 继续跳转
                        _navigation(context, postcard, requestCode, callback);
                    }
    
                    /**
                     * Interrupt process, pipeline will be destory when this method called.
                     *
                     * @param exception Reson of interrupt.
                     */
                    @Override
                    public void onInterrupt(Throwable exception) {
                        if (null != callback) {
                          // 回调
                            callback.onInterrupt(postcard);
                        }
    
                        logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                    }
                });
            } else {
                return _navigation(context, postcard, requestCode, callback);
            }
    
            return null;
        }
    

    拦截器的处理是通过interceptorService代理来实现, 我们可以往里面看

    @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 (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 {
                            // 通过interceptorCounter倒数计数器, 一个个执行拥有的拦截器
                            _excute(0, interceptorCounter, postcard);
                            // 默认timeout时间是0.3s
                            interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                            // 如果在postcard.getTimeout()时间内没有执行完
                            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);
            }
        }
    
        /**
         * Excute interceptor
         *
         * @param index    current interceptor index
         * @param counter  interceptor counter
         * @param postcard routeMeta
         */
        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) {
                        // Last interceptor excute over with fatal exception.
    
                        postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                        counter.cancel();
                        // Be attention, maybe the thread in callback has been changed,
                        // then the catch block(L207) will be invalid.
                        // The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
    //                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {    // You shouldn't throw the exception if the thread is main thread.
    //                        throw new HandlerException(exception.getMessage());
    //                    }
                    }
                });
            }
        }
    
        @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();
                        }
                    }
                }
            });
        }
    
        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() + "]");
                    }
                }
            }
        }
    }
    

    ok, 在进行了拦截器处理(或者没有拦截器的情况下), 我们走到了_navigation(context, postcard, requestCode, callback), 到这里基本又是熟悉的配方了, 可以看到他最后通过startActivity来进行跳转.

    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 ((-1 != postcard.getEnterAnim() && -1 != 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;
        }
    

    相关文章

      网友评论

      本文标题:ARouter源码解析

      本文链接:https://www.haomeiwen.com/subject/xzbgdftx.html