ARouter 源码分析之 Group 和 Path

首先我们都知道 ARouter是以路由的方式实现组件间(组件化项目)通讯的的框架。

路由的本质,是映射和寻址,收集所有的注册类,生成字符串和注册类的映射关系,这样就可以通过字符串找到对应的类.

解决的问题,没有依赖关系的两个模块,不能直接交互,路由提供了仓库,可以通过字符串找到注入仓库的类,解决类模块间交互的问题(组件间通讯)

如何分组和构建路由表的呢?

一.从使用说起

  1. 我们按照文档使用ARouter 的时候注意到, 使用@Route注解的模块,需要在modeule 的build.gradle里添加:

    defaultConfig {
       javaCompileOptions {
           annotationProcessorOptions {
               arguments = [AROUTER_MODULE_NAME: project.getName()]
           }
       }
    }
    

没有这段代码会在build项目时爆错:
These no module name, at 'build.gradle', like : ...

  1. 必须在每个使用@Route 注解的模块里都引入ARouter的注解处理器,否则这个模块里注解不会被处理

    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
    
  2. @Route 注解的path 至少需要有两级

    @Route(path = "/test/activity")
    public class YourActivity extend Activity {
        ...
    }
    

    否则toast提示:“There's no route matched! Path = [/xxx/xxx] Group = [xxxx]”

编译生成的类:


root类

Root类


24938213e1e7af99c8fa3aa0.png

group类
24938213ebe75c033fba54dc.png
* ARouter$Root$$xxx(modulename) 把所有的组(ARouter$Group$xxx) put到Map集合里(routers)

* ARouter$Group$$xxx(groupname) 把一个分组下的所有路径(RouteMeta)存入map

* ARouter$Providers$$xxx(modulename) 把注册的接口存入map

二.ARouter 注解处理器:RouteProcessor

有注解就有注解处理器,ARouter也是基于APT,构建路由表的逻辑就在RouteProcessor,也是在RouteProcessor里生成了上面的那些类

APT 和 javapoat 有同学分享过,这也是APT 和 javapoat的一次应用

  1. BaseProcessor

    RouteProcessor 继承了 BaseProcessor

     public abstract class BaseProcessor extends AbstractProcessor {
         ...
         // 模块名
         String moduleName = null;
         //是否需要生成router 文档
         boolean generateDoc;
    
         @Override
         public synchronized void init(ProcessingEnvironment processingEnv) {
             super.init(processingEnv);
             //初始化工具类
             mFiler = processingEnv.getFiler();
             types = processingEnv.getTypeUtils();
             elementUtils = processingEnv.getElementUtils();
             typeUtils = new TypeUtils(types, elementUtils);
             logger = new Logger(processingEnv.getMessager());
    
             // Attempt to get user configuration [moduleName]
             Map<String, String> options = processingEnv.getOptions();
             if (MapUtils.isNotEmpty(options)) {
    
                 //从options里获取 moduleName
                 moduleName = options.get(KEY_MODULE_NAME);
                 generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
             }
    
             if (StringUtils.isNotEmpty(moduleName)) {
                 moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
    
             } else {
                。。。
             }
         }
    
         ...
    
         @Override
         public Set<String> getSupportedOptions() {
             return new HashSet<String>() {{
                 this.add(KEY_MODULE_NAME);
                 this.add(KEY_GENERATE_DOC_NAME);
             }};
         }
    }
    

    主要初始化工具类,从gradle 配置里获取 moduleName

  • getSupportedOptions()

    KEY_MODULE_NAME的值:

      // Options of processor
      public static final String KEY_MODULE_NAME = "AROUTER_MODULE_NAME";
      public static final String KEY_GENERATE_DOC_NAME = "AROUTER_GENERATE_DOC";
    

    就是我们在build.gradle里配置的 AROUTER_MODULE_NAME


    2493821365b453ece9e5cca9.png
  • init()
    从 options 里获取 moduleName,如果moduleName为空抛出异常; 异常信息就是这段字符串:

    2493821354ae74e74f7443f0.png

    这就是为什么如果不在build.gradle里配置AROUTER_MODULE_NAME,会异常的原因, moduleName有什么用?

  1. RouteProcessor

    public class RouteProcessor extends BaseProcessor {
        private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.
        private Map<String, String> rootMap = new TreeMap<>();  // Map of root metas, used for generate class file in order.
    
        private TypeMirror iProvider = null;
    
        @Override
        public synchronized void init(ProcessingEnvironment processingEnv) {
            super.init(processingEnv);
            //
            iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            if (CollectionUtils.isNotEmpty(annotations)) {
                Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
                    try {
                        this.parseRoutes(routeElements);
                    } catch (Exception e) {
                    }
                    return true;
                }
    
                return false;
        }
    
    }
    

    创建两个map,分别用来存放当前模块下所有的分组(rootMap),和每个分组下的路径(groupMap)

    private Map<String, Set<RouteMeta>> groupMap = new HashMap<>();
    private Map<String, String> rootMap = new TreeMap<>();

    process()方法调用了parseRoutes()方法,处理注解的逻辑在这个方里

    获取activity/fragment/service 的类型,用于后面的类型判断

    private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
        if (CollectionUtils.isNotEmpty(routeElements)) {
            // prepare the type an so on.
    
            logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
    
            rootMap.clear();
    
            TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
            TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
            TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
            TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
    
            // Interface of ARouter
            TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
            TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
            ClassName routeMetaCn = ClassName.get(RouteMeta.class);
            ClassName routeTypeCn = ClassName.get(RouteType.class);
    

    创建 RouteMeta 对象,RouteMeta主要存放的是路径信息,包含了Rout注解的值,activity/fragment/service枚举,className,以及跳转参数信息。

        for (Element element : routeElements) {
            TypeMirror tm = element.asType();
            Route route = element.getAnnotation(Route.class);
            RouteMeta routeMeta;
    
            //6。注解的是 Activity or Fragment
            if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
                // Get all fields annotation by @Autowired
                Map<String, Integer> paramsType = new HashMap<>();
                Map<String, Autowired> injectConfig = new HashMap<>();
                // 7。收集 @Autowired 注解的参数
                injectParamCollector(element, paramsType, injectConfig);
    
                if (types.isSubtype(tm, type_Activity)) {
                    // Activity
                    logger.info(">>> Found activity route: " + tm.toString() + " <<<");
                    routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
                } else {
                    // Fragment
                    logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
                    routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
                }
    
                routeMeta.setInjectConfig(injectConfig);
            } else if (types.isSubtype(tm, iProvider)) {
                // IProvider(接口)
                logger.info(">>> Found provider route: " + tm.toString() + " <<<");
                routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
            } else if (types.isSubtype(tm, type_Service)) {
                // Service
                logger.info(">>> Found service route: " + tm.toString() + " <<<");
                routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
            } else {
                throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
            }
            // 7。routeMeta(路径信息)存入groupMap
            categories(routeMeta);
        }
    

    1.先判断路径path是否合法;然后把RouteMeta按groupName分组存入 groupMap

    private void categories(RouteMeta routeMete) {
        //验证routeMete
        if (routeVerify(routeMete)) {
            logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
            Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
            if (CollectionUtils.isEmpty(routeMetas)) {
                Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {
                    @Override
                    public int compare(RouteMeta r1, RouteMeta r2) {
                        try {
                            return r1.getPath().compareTo(r2.getPath());
                        } catch (NullPointerException npe) {
                            logger.error(npe.getMessage());
                            return 0;
                        }
                    }
                });
                routeMetaSet.add(routeMete);
                groupMap.put(routeMete.getGroup(), routeMetaSet);
            } else {
                routeMetas.add(routeMete);
            }
        } else {
            logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
        }
    }     
    

判断路径方法 ,从下面这段代码知道了
path 必须“/”开头,并且第一段作为默认 group 名;

private boolean routeVerify(RouteMeta meta) {
    String path = meta.getPath();

    if (StringUtils.isEmpty(path) || !path.startsWith("/")) {   // The path must be start with '/' and not empty!
        return false;
    }

    if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)
        try {
            String defaultGroup = path.substring(1, path.indexOf("/", 1));
            if (StringUtils.isEmpty(defaultGroup)) {
                return false;
            }

            meta.setGroup(defaultGroup);
            return true;
        } catch (Exception e) {
            logger.error("Failed to extract default group! " + e.getMessage());
            return false;
        }
    }

    return true;
}

开始生成代码

 for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
            //组名
            String groupName = entry.getKey();
              // 构建方法
                /** 
                *  @Override
                *  public void loadInto(Map<String, RouteMeta> providers);
                */
            MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(groupParamSpec);

            List<RouteDoc> routeDocList = new ArrayList<>();
            Set<RouteMeta> groupData = entry.getValue();
            for (RouteMeta routeMeta : groupData) {
                // Build group method body
                RouteDoc routeDoc = extractDocInfo(routeMeta);

                ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());


                // 构建跳转参数的map
                StringBuilder mapBodyBuilder = new StringBuilder();
                Map<String, Integer> paramsType = routeMeta.getParamsType();
                Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();
                if (MapUtils.isNotEmpty(paramsType)) {
                    List<RouteDoc.Param> paramList = new ArrayList<>();

                    for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
                        mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");

                        RouteDoc.Param param = new RouteDoc.Param();
                        Autowired injectConfig = injectConfigs.get(types.getKey());
                        param.setKey(types.getKey());
                        param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
                        param.setDescription(injectConfig.desc());
                        param.setRequired(injectConfig.required());

                        paramList.add(param);
                    }

                    routeDoc.setParams(paramList);
                }
                String mapBody = mapBodyBuilder.toString();

                //生成 map的 put 代码
                loadIntoMethodOfGroupBuilder.addStatement(
                        "atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                        routeMeta.getPath(),
                        routeMetaCn,
                        routeTypeCn,
                        className,
                        routeMeta.getPath().toLowerCase(),
                        routeMeta.getGroup().toLowerCase());

                routeDoc.setClassName(className.toString());
                routeDocList.add(routeDoc);
            }

            // 生成ARouter$Group$文件
            String groupFileName = NAME_OF_GROUP + groupName;
            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                    TypeSpec.classBuilder(groupFileName)
                            .addJavadoc(WARNING_TIPS)
                            .addSuperinterface(ClassName.get(type_IRouteGroup))
                            .addModifiers(PUBLIC)
                            .addMethod(loadIntoMethodOfGroupBuilder.build())
                            .build()
            ).build().writeTo(mFiler);

            logger.info(">>> Generated group: " + groupName + "<<<");
            rootMap.put(groupName, groupFileName);
            docSource.put(groupName, routeDocList);
        }

上面这段代码生成类 ARouter$$Group$login,groupname = login;

public class ARouter$$Group$xxx(groupname) implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/login/loginX", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginx", "login", null, -1, -2147483648));
        atlas.put("/login/register", RouteMeta.build(RouteType.ACTIVITY, RegisterActivity.class, "/login/register", "login", null, -1, -2147483648));
    }
}

有多少group就有多少这样的类,然后把这些类名存入rootMap

 if (MapUtils.isNotEmpty(rootMap)) {
            // Generate root meta by group name, it must be generated before root, then I can find out the class of group.
            for (Map.Entry<String, String> entry : rootMap.entrySet()) {
                loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
            }
        }

        // Write provider into disk
        String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
        JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                TypeSpec.classBuilder(providerMapFileName)
                        .addJavadoc(WARNING_TIPS)
                        .addSuperinterface(ClassName.get(type_IProviderGroup))
                        .addModifiers(PUBLIC)
                        .addMethod(loadIntoMethodOfProviderBuilder.build())
                        .build()
        ).build().writeTo(mFiler);

上面这段代码,我们看到了moduleName,也就是gradle里的project.getName(),这段代码生成下面这个类, moduleName =“LoginSDK”

public class ARouter$$Providers$$LoginSDK implements IProviderGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> providers) {
        providers.put("com.example.loginsdk.IUserInfo", RouteMeta.build(RouteType.PROVIDER, UserInfoManager.class, "/interface/user", "interface", null, -1, -2147483648));
    }
}

可以看到这个类命名:ARouter+Providers+moduleName,Module里只有一个这样的类,把这个模块里注册的接口(接口都继承IProvider)put到map集合里

接着生成root

String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
        JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                TypeSpec.classBuilder(rootFileName)
                        .addJavadoc(WARNING_TIPS)
                        .addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
                        .addModifiers(PUBLIC)
                        .addMethod(loadIntoMethodOfRootBuilder.build())
                        .build()
        ).build().writeTo(mFiler);

rootFileName =“ARouter$$Root$LoginSDK”,其中moduleName = LoginSDK

最终生成的类是:

public class ARouter$$Root$$LoginSDK implements IRouteRoot {
    @Override
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        routes.put("interface", ARouter$$Group$$interface.class);
        routes.put("login", ARouter$$Group$$login.class);
    }
} 

这个类也只有一个,把前面生成的所有 ARouter$Group 都类存入map,到此就完所有注解的分组和映射。

模块间以modleName分离,每个模块有多个组,分组能够更好的管理和查找路由信息。

映射关系构建好了,类文件创建出来, 什么时候加载调用的呢?

三、ARouter 初始化

一般在application里初始化

public class MainApplication  extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        ARouter.openDebug();
        ARouter.init(this);
    }

开发过程中如果没有 开启 ARouter.openDebug();会发现新增的path 跳转不了,但是release可以跳转

来看看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.init(application)

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(mContext, executor),并传入了一个线程池

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    executor = tpe;

    try {
        long startInit = System.currentTimeMillis();
        //load by plugin first
        loadRouterMap();
        //使用gradle插件自动处理了
        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)) {
                // These class was generated by arouter-compiler.
                //通过指定包名,扫描包下面包含的所有的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);    // 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>()));
            }
            。。。
            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);
                }
            }
        }
        。。。
    } catch (Exception e) {
        throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
    }
}

有个getFileNameByPackageName()方法

 public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
        final Set<String> classNames = new HashSet<>();

        List<String> paths = getSourcePaths(context);
        final CountDownLatch parserCtl = new CountDownLatch(paths.size());

        for (final String path : paths) {
            DefaultPoolExecutor.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    DexFile dexfile = null;

                    try {
                        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);
                        }

                        Enumeration<String> dexEntries = dexfile.entries();
                        while (dexEntries.hasMoreElements()) {
                            String className = dexEntries.nextElement();
                            if (className.startsWith(packageName)) {
                                classNames.add(className);
                            }
                        }
                     。。。   
                }
            });
        }

        parserCtl.await();
        return classNames;
    }

通过指定包名,扫描包下面所有的文件ClassName;在编译阶段注解处理器在“com.alibaba.android.arouter.routes”这个包下面生成了文件,通过这个方法就可以扫描这个包下所有的类名;扫描的操作是运行在线程池里的,与_ARouter里的线程池是同一个

在这之前有个判断条件:
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)),如果开启了ARouter.openDebug()或者 项目的版本有变化,则重新生成routerMap并且存入sp,否则直接用sp里的缓存,新增的path不会被加载,这解释了上面出现不能跳转的问题。

继续

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);
        }
}

可以看到init()里加载了 ARouter$Providers$、ARouter$Root、ARouter$Interceptors,但是没有加载任何一个ARouter$Group;那group是什么时候加载的呢

四、navigation()

如何根据path找到跳转目标(寻址)?

ARouter.getInstance().build("login/loginX").navigation();

build()会进入 _ARouter.build(String path)

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), true);
    }
}

这里有个方法,可以看到path 必须以“/”开头且至少两段,否则会抛异常

    private String extractGroup(String path) {
        if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
            throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
        }

        try {
            String defaultGroup = path.substring(1, path.indexOf("/", 1));
            if (TextUtils.isEmpty(defaultGroup)) {
                throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
            } else {
                return defaultGroup;
            }
        } catch (Exception e) {
            logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
            return null;
        }
    }

build()方法创建了Postcard对象,然后看Postcard.navigation()

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
            // Pretreatment failed, navigation canceled.
            return null;
        }

        // Set context to postcard.
        postcard.setContext(null == context ? mContext : context);

        try {
            LogisticsCenter.completion(postcard);

这里调用了 LogisticsCenter.completion(postcard) ,找到跳转目标,给postcart赋值

public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }

    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        // Maybe its does't exist, or didn't load.
        if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {
    
            try {
                。。。
                addRouteGroupDynamic(postcard.getGroup(), null);

                。。。
            } catch (Exception e) {
        。。。
            }

            completion(postcard);   // Reload
        }
    } else {
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        。。。

    }
}

Warehouse.routes如果已经加载了path,直接赋值给postcard,如果没找到但是在Warehouse.groupsIndex里有这个group(Warehouse.groupsIndex是在init()里添加元素的,ARouter$$Root$xxx这个类的loadInto()方法里) 就会执行
addRouteGroupDynamic(postcard.getGroup(), null)方法

public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    if (Warehouse.groupsIndex.containsKey(groupName)){
        // If this group is included, but it has not been loaded
        // load this group first, because dynamic route has high priority.
        Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
        Warehouse.groupsIndex.remove(groupName);
    }

    // cover old group.
    if (null != group) {
        group.loadInto(Warehouse.routes);
    }
}

Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
加载一个ARouter$$Group$xxx 类,调用其loadInto()方法给Warehouse.routes添加元素 ,寻址完成。

看到这里我们就知道了ARouter$Group$xxx是在调用ARouter.getInstance().build(“/group/path”).navigation()时候加载,使用时才加载。

跳转:
_ARouter._navigation().startActivity()

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = postcard.getContext();

    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 (0 != flags) {
                intent.setFlags(flags);
            }

            // Non activity, need FLAG_ACTIVITY_NEW_TASK
            if (!(currentContext instanceof Activity)) {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

            // Set Actions
            String action = postcard.getAction();
            if (!TextUtils.isEmpty(action)) {
                intent.setAction(action);
            }

            // Navigation in main looper.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

总结

  1. ARouter 编译期间构建路由表和映射关系,分模块分组;
  2. 有自己的线程池,编译完成后扫描dex文件,扫出apt生成的文件的类名
  3. 初始化只加载了 ARouter$Providers$、ARouter$Root$、ARouter$Interceptors,;ARouter$Group 在第一次使用时加载。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容