ARouter 源碼分析之 Group 和 Path

首先我們都知道 ARouter是以路由的方式實(shí)現(xiàn)組件間(組件化項(xiàng)目)通訊的的框架鳞溉。

路由的本質(zhì)务豺,是映射和尋址棒旗,收集所有的注冊(cè)類(lèi)痊乾,生成字符串和注冊(cè)類(lèi)的映射關(guān)系皮壁,這樣就可以通過(guò)字符串找到對(duì)應(yīng)的類(lèi).

解決的問(wèn)題,沒(méi)有依賴(lài)關(guān)系的兩個(gè)模塊哪审,不能直接交互蛾魄,路由提供了倉(cāng)庫(kù),可以通過(guò)字符串找到注入倉(cāng)庫(kù)的類(lèi)湿滓,解決類(lèi)模塊間交互的問(wèn)題(組件間通訊)

如何分組和構(gòu)建路由表的呢滴须?

一.從使用說(shuō)起

  1. 我們按照文檔使用ARouter 的時(shí)候注意到, 使用@Route注解的模塊茉稠,需要在modeule 的build.gradle里添加:

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

沒(méi)有這段代碼會(huì)在build項(xiàng)目時(shí)爆錯(cuò):
These no module name, at 'build.gradle', like : ...

  1. 必須在每個(gè)使用@Route 注解的模塊里都引入ARouter的注解處理器描馅,否則這個(gè)模塊里注解不會(huì)被處理

    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
    
  2. @Route 注解的path 至少需要有兩級(jí)

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

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

編譯生成的類(lèi):


root類(lèi)

Root類(lèi)


24938213e1e7af99c8fa3aa0.png

group類(lèi)
24938213ebe75c033fba54dc.png
* ARouter$Root$$xxx(modulename) 把所有的組(ARouter$Group$xxx) put到Map集合里(routers)

* ARouter$Group$$xxx(groupname) 把一個(gè)分組下的所有路徑(RouteMeta)存入map

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

二.ARouter 注解處理器:RouteProcessor

有注解就有注解處理器,ARouter也是基于APT而线,構(gòu)建路由表的邏輯就在RouteProcessor铭污,也是在RouteProcessor里生成了上面的那些類(lèi)

APT 和 javapoat 有同學(xué)分享過(guò)恋日,這也是APT 和 javapoat的一次應(yīng)用

  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);
             //初始化工具類(lèi)
             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);
             }};
         }
    }
    

    主要初始化工具類(lèi),從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";
    

    就是我們?cè)赽uild.gradle里配置的 AROUTER_MODULE_NAME


    2493821365b453ece9e5cca9.png
  • init()
    從 options 里獲取 moduleName磅网,如果moduleName為空拋出異常谈截; 異常信息就是這段字符串:

    2493821354ae74e74f7443f0.png

    這就是為什么如果不在build.gradle里配置AROUTER_MODULE_NAME,會(huì)異常的原因涧偷, 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;
        }
    
    }
    

    創(chuàng)建兩個(gè)map,分別用來(lái)存放當(dāng)前模塊下所有的分組(rootMap)燎潮,和每個(gè)分組下的路徑(groupMap)

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

    process()方法調(diào)用了parseRoutes()方法喻鳄,處理注解的邏輯在這個(gè)方里

    獲取activity/fragment/service 的類(lèi)型,用于后面的類(lèi)型判斷

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

    創(chuàng)建 RouteMeta 對(duì)象确封,RouteMeta主要存放的是路徑信息除呵,包含了Rout注解的值,activity/fragment/service枚舉爪喘,className颜曾,以及跳轉(zhuǎn)參數(shù)信息。

        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 注解的參數(shù)
                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) {
        //驗(yàn)證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 必須“/”開(kāi)頭,并且第一段作為默認(rèn) 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;
}

開(kāi)始生成代碼

 for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
            //組名
            String groupName = entry.getKey();
              // 構(gòu)建方法
                /** 
                *  @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());


                // 構(gòu)建跳轉(zhuǎn)參數(shù)的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);
        }

上面這段代碼生成類(lèi) 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就有多少這樣的類(lèi)荠雕,然后把這些類(lèi)名存入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()驶赏,這段代碼生成下面這個(gè)類(lèi)炸卑, 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));
    }
}

可以看到這個(gè)類(lèi)命名:ARouter+Providers+moduleName,Module里只有一個(gè)這樣的類(lèi)煤傍,把這個(gè)模塊里注冊(cè)的接口(接口都繼承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

最終生成的類(lèi)是:

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

這個(gè)類(lèi)也只有一個(gè),把前面生成的所有 ARouter$Group 都類(lèi)存入map蚯姆,到此就完所有注解的分組和映射五续。

模塊間以modleName分離洒敏,每個(gè)模塊有多個(gè)組,分組能夠更好的管理和查找路由信息疙驾。

映射關(guān)系構(gòu)建好了凶伙,類(lèi)文件創(chuàng)建出來(lái), 什么時(shí)候加載調(diào)用的呢它碎?

三函荣、ARouter 初始化

一般在application里初始化

public class MainApplication  extends Application {

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

開(kāi)發(fā)過(guò)程中如果沒(méi)有 開(kāi)啟 ARouter.openDebug();會(huì)發(fā)現(xiàn)新增的path 跳轉(zhuǎn)不了,但是release可以跳轉(zhuǎn)

來(lái)看看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.");
    }

調(diào)用里 _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 又調(diào)用了 LogisticsCenter.init(mContext, executor)扳肛,并傳入了一個(gè)線程池

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插件自動(dòng)處理了
        if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            //手動(dòng)處理
            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.
                //通過(guò)指定包名傻挂,掃描包下面包含的所有的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() + "]");
    }
}

有個(gè)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;
    }

通過(guò)指定包名害驹,掃描包下面所有的文件ClassName鞭呕;在編譯階段注解處理器在“com.alibaba.android.arouter.routes”這個(gè)包下面生成了文件,通過(guò)這個(gè)方法就可以?huà)呙柽@個(gè)包下所有的類(lèi)名宛官;掃描的操作是運(yùn)行在線程池里的葫松,與_ARouter里的線程池是同一個(gè)

在這之前有個(gè)判斷條件:
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)),如果開(kāi)啟了ARouter.openDebug()或者 項(xiàng)目的版本有變化底洗,則重新生成routerMap并且存入sp腋么,否則直接用sp里的緩存,新增的path不會(huì)被加載亥揖,這解釋了上面出現(xiàn)不能跳轉(zhuǎn)的問(wèn)題珊擂。

繼續(xù)

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摧扇,但是沒(méi)有加載任何一個(gè)ARouter$Group;那group是什么時(shí)候加載的呢

四、navigation()

如何根據(jù)path找到跳轉(zhuǎn)目標(biāo)(尋址)挚歧?

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

build()會(huì)進(jìn)入 _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);
    }
}

這里有個(gè)方法扛稽,可以看到path 必須以“/”開(kāi)頭且至少兩段,否則會(huì)拋異常

    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()方法創(chuàng)建了Postcard對(duì)象滑负,然后看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);

這里調(diào)用了 LogisticsCenter.completion(postcard) 在张,找到跳轉(zhuǎn)目標(biāo)锡搜,給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如果已經(jīng)加載了path,直接賦值給postcard佣蓉,如果沒(méi)找到但是在Warehouse.groupsIndex里有這個(gè)group(Warehouse.groupsIndex是在init()里添加元素的披摄,ARouter$$Root$xxx這個(gè)類(lèi)的loadInto()方法里) 就會(huì)執(zhí)行
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);
加載一個(gè)ARouter$$Group$xxx 類(lèi),調(diào)用其loadInto()方法給Warehouse.routes添加元素 勇凭,尋址完成疚膊。

看到這里我們就知道了ARouter$Group$xxx是在調(diào)用ARouter.getInstance().build(“/group/path”).navigation()時(shí)候加載,使用時(shí)才加載虾标。

跳轉(zhuǎn):
_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);
                }
            });

總結(jié)

  1. ARouter 編譯期間構(gòu)建路由表和映射關(guān)系寓盗,分模塊分組;
  2. 有自己的線程池璧函,編譯完成后掃描dex文件傀蚌,掃出apt生成的文件的類(lèi)名
  3. 初始化只加載了 ARouter$Providers$、ARouter$Root$蘸吓、ARouter$Interceptors善炫,;ARouter$Group 在第一次使用時(shí)加載。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末美澳,一起剝皮案震驚了整個(gè)濱河市销部,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌制跟,老刑警劉巖舅桩,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異雨膨,居然都是意外死亡擂涛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撒妈,“玉大人恢暖,你說(shuō)我怎么就攤上這事≌遥” “怎么了杰捂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)棋蚌。 經(jīng)常有香客問(wèn)我嫁佳,道長(zhǎng),這世上最難降的妖魔是什么谷暮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任蒿往,我火速辦了婚禮,結(jié)果婚禮上湿弦,老公的妹妹穿的比我還像新娘瓤漏。我一直安慰自己,他們只是感情好颊埃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蔬充。 她就那樣靜靜地躺著,像睡著了一般竟秫。 火紅的嫁衣襯著肌膚如雪娃惯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天肥败,我揣著相機(jī)與錄音,去河邊找鬼愕提。 笑死馒稍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浅侨。 我是一名探鬼主播纽谒,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼如输!你這毒婦竟也來(lái)了鼓黔?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤不见,失蹤者是張志新(化名)和其女友劉穎澳化,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體稳吮,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缎谷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灶似。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片列林。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瑞你,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出希痴,到底是詐尸還是另有隱情者甲,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布砌创,位于F島的核電站过牙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纺铭。R本人自食惡果不足惜寇钉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舶赔。 院中可真熱鬧扫倡,春花似錦、人聲如沸竟纳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锥累。三九已至缘挑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桶略,已是汗流浹背语淘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留际歼,地道東北人惶翻。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鹅心,于是被迫代替她去往敵國(guó)和親吕粗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. ARouter 原理概述 ARouter 是阿里開(kāi)源的一款幫助 Android APP 進(jìn)行組件化改造的路由...
    燈不利多閱讀 2,304評(píng)論 2 9
  • 隨著面試和工作中多次遇到ARouter的使用問(wèn)題,我決定把ARouter的源碼從頭到尾理一遍输枯。讓我瞧瞧你到底有幾斤...
    吃人的鍋閱讀 1,300評(píng)論 1 12
  • Arouter框架適合項(xiàng)目比較大议泵,模塊多的時(shí)候,可以實(shí)現(xiàn)解耦用押,不需要知道跳轉(zhuǎn)的是哪個(gè)activity肢簿,只需要知道配...
    破曉11閱讀 3,462評(píng)論 0 2
  • ARouter探究(一) 前言 ARouter 是 Alibaba 開(kāi)源的一款 Android 頁(yè)面路由框架,特別...
    Jason騎蝸牛看世界閱讀 1,329評(píng)論 1 3
  • @Route 在組件化池充,模塊化過(guò)程中桩引,經(jīng)常會(huì)遇到不同的模塊之間進(jìn)行頁(yè)面跳轉(zhuǎn),通信等收夸;ARouter 提供了 @Ro...
    你怕是很皮哦閱讀 1,119評(píng)論 0 2