ARouter 之@Route源码分析

@Route

在组件化,模块化过程中,经常会遇到不同的模块之间进行页面跳转,通信等;ARouter 提供了 @Route 注解来简化这一过程。

定义

先来看看 @Route 的定义。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {

    // 路径
    String path();

    // 组名,如果不指定,会取路径上的第一层级作为组名
    String group() default "";

    // 生成doc 用的
    String name() default "";

    int extras() default Integer.MIN_VALUE;

    // 优先级,值越小,优先级越高
    int priority() default -1;
}
  1. path() 用来定义路径,必须以 / 开头;
  2. group() 用来划分组,如果不指定,则默认取 path() 的第一级;
  3. name() 是生成文档时使用的;
  4. extras() 配置一些额外的信息;
  5. priority() 定义优先级;

下面我们通过几个简单的例子,来看看 @Route 在实际开发中的作用。

路由

@Route 可以简化跨模块之间的路由。

举个栗子,在模块 A 中有 AActivity,模块 B 中有 BActivity ,这时 AActivity 想要跳转 BActivity,是不能直接通过显式的 Intent 进行跳转的,当然我们也可以通过隐示的 Intent 进行跳转,但是一个项目有成百上千个页面,这样做无疑增加了维护的成本。

这个时候我们可以通过 @RouteBActivity 增加一些配置。

@Route(path = "/b/activity1")
public class BActivity extends AppCompatActivity {
}

这样一来,AActivity 跳转 BActivity 只需要通过 ARouter 暴露的 API 就可以轻松实现跨模块跳转了。

ARouter.getInstance().build("/b/activity1")
        .withString("xxx", "xxx")
        .withInt("xxx", xxx)
        .withSerializable("xxx", xxx)
        .withParcelable("xxx", xxx)
        .withObject("xxx", xxx)
        .navigation();

通信

@Route 可以简化跨模块之间的通信。

模块与模块之间可能不仅仅只是简单的页面路由等,或多或少都会存在一些数据访问等操作。

举个栗子,模块 A 需要访问 模块 B 的一些数据,我们可以通过 @RouteIProvider 轻松简化这一过程。

public interface IBService extends IProvider {
    void hello();
}

@Route(path = "/b/service")
public class BServiceImpl implements IBService {
    @Override
    public void init(Context context) {
    }

    @Override
    public void hello() {
        Log.d("ppdai", "hello");
    }
}

定义模块 B 想要暴露给其他模块的服务 IBService,并让其继承 IProvider,然后在模块 B 中定义实现 BServiceImpl,通过 @Route 注解注册路由信息,这个时候模块 A 就可以通过相同的 API 访问 B 模块的东西。

BServiceImpl bService = (BServiceImpl) ARouter.getInstance().build("/b/service").navigation();
bService.hello();

源码分析

接下来具体分析一下源码,来看看 ARouter 是如何实现的。

首先,我们先来看看初始化过程,通常我们会在 Application 中对 ARouter 进行初始化操作。

ARouter.init();

那么,初始化它究竟初始化了什么呢?我们先来看看 init()

public static void init(Application application) {
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        // 调用 _ARouter.init()
        hasInit = _ARouter.init(application);

        if (hasInit) {
            _ARouter.afterInit();
        }

        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
    }
}

这里实际上是调用了 _ARouter.init()

protected static synchronized boolean init(Application application) {
    mContext = application;
    // 调用 LogisticsCenter.init()
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;
    mHandler = new Handler(Looper.getMainLooper());

    return true;
}

然后在 _ARouter.init() 中调用了 LogisticsCenter.init()

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    try {
        loadRouterMap();
        if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            Set<String> routerMap;

            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                // 1. 调用 getFileNameByPackageName() 加载 路由配置信息
                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 {
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }

            // 2. 遍历加载路由之后存放的类
            for (String className : routerMap) {
                // 2.1 如果是 com.alibaba.android.arouter.routes.ARouter$$Root ...
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // 2.1.1 调用其 loadInfo() ,并将信息缓存到 Warehouse 里面
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // 2.2 如果是 com.alibaba.android.arouter.routes.ARouter$$Interceptors .. 调用其 loadInfo() 并将信息存放到 Warehouse 里面
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // 2.3 如果是 com.alibaba.android.arouter.routes.ARouter$$Providers.. 调用其 loadInfo() 并将信息存放到 WareHouse 里面
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
        }

    } catch (Exception e) {
        throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
    }
}

这个方法主要做了两件事情

  1. 调用 ClassUtils.getFileNameByPackageName() 查找所有在编译期通过注解处理器生成的类,并将其存放到 routerMap 中,这里的 getFileNameByPackageName() 就不具体看了,感兴趣的童鞋可以自行研究一下,主要是找 dex 里面 com.alibaba.android.arouter.routes 包下面的类,这些类都是通过 APT 在编译时期生成的,后面我们分析 RouteProcessor 的时候就明白了;
  2. 通过 for 循环遍历 routerMap 分别调用其 loadInfo() 将路由配置信息存放到 Warehouse 中;

所以,在初始化的过程中,ARouter 就将所有的路由信息都存放到了 Warehouse 中。

接下来,我们再来看看分析一下使用过程,这里我们以 Activity 的路由为例。

ARouter.getInstance().build("/b/activity1").navigation(context)

这里 build() 的时候会构建一个 Postcard 对象,然后调用其 navigation(),我们来看看 navigation() 做了什么。

public Object navigation(Context context) {
    return navigation(context, null);
}

public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
}

public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
    return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}

从调用链可以看到,它最终调用到了 _ARouternavigation(),我们接着往下看。

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

    try {
        // 1. 找到 path 对应的 路由信息
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());

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

    if (!postcard.isGreenChannel()) {
        // 通过 isGreenChannel() 判断是否需要走拦截器逻辑
        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;
}

这里先通过 LogisticsCenter.completion() 找到 path 对应的路由信息,然后通过 isGreenChannel() 决定是否走到拦截器里面,拦截器逻辑不在本文分析之内,后面会单独写一篇文章分析拦截器的原理。我们接着看 _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)) {
                // 使用非 Activity 的 context 启动 Activity,需要添加 FLAG_ACTIVITY_NEW_TASK 标记
                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() {
                    // 启动 Activity
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;
        // ...
        default:
            return null;
    }

    return null;
}

可以看到 _navigation() 中对 Intent 进行了组装,然后调用了 startActivity(),这个方法我们就不分析了,就是通过 context 去启动对应的 Activity 了。

RouteProcessor

没错,RouteProcessor 就是 @Route 注解的处理器了,如此强大的功能, 我们还真得好好研究一下它。

我们先看看 init() 方法做了什么

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    // 1. 判断是否需要生成路由的 json 文件
    if (generateDoc) {
        try {
            // 如果使用的是 kapt ,这个文件是在 build/generated/source/kapt/[buildVariant]/com.alibaba.android.arouter.docs
            docWriter = mFiler.createResource(
                    StandardLocation.SOURCE_OUTPUT,
                    PACKAGE_OF_GENERATE_DOCS,
                    "arouter-map-of-" + moduleName + ".json"
            ).openWriter();
        } catch (IOException e) {
            logger.error("Create doc writer failed, because " + e.getMessage());
        }
    }

    // 2. 获取 IProvider 的 TypeMirror
    iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
}

init() 方法做的事情很简单;

  1. 使用 generateDoc 字段判断是否需要生成 arouter-map-of-modulename.json 文件,并生成对应的 Writer
  2. 获取到 IProviderTypeMirror

这里的 generateDoc 我们看一下父类 BaseProcessorinit() 方法,就知道是个什么情况了。

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    // ...
    Map<String, String> options = processingEnv.getOptions();
    if (MapUtils.isNotEmpty(options)) {
        moduleName = options.get(KEY_MODULE_NAME);
        // 获取在 build.gradle 里面配置的参数 AROUTER_GENERATE_DOC ,判断是否是 enable
        generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
    }
    // ...
}

也就是说,如果我们需要生成路由的 json 文件,需要在 build.gradle 中新增如下配置。

kapt {
    arguments {
        // ...
        // 配置需要生成路由的 json 文件
        arg("AROUTER_GENERATE_DOC", "enable")
    }
}

然后编译项目,就可以看到生成的 json 文件了。

{
    "logistic":[
        {
            "group":"logistic",
            "path":"/logistic/detail",
            "className":"com.mockuai.module.order.logistic.detail.LogisticDetailActivity",
            "type":"activity",
            "mark":-2147483648
        },
        {
            "group":"logistic",
            "path":"/logistic/list",
            "className":"com.mockuai.module.order.logistic.list.LogisticListActivity",
            "type":"activity",
            "mark":-2147483648
        }
    ],
    "order":[
        {
            "group":"order",
            "path":"/order/distribution/detail",
            "className":"com.mockuai.module.order.distribution.detail.DistributionOrderDetailActivity",
            "type":"activity",
            "mark":-2147483648
        },
        {
            "group":"order",
            "path":"/order/sample/list",
            "className":"com.mockuai.module.order.sample.list.SampleOrderListActivity",
            "type":"activity",
            "mark":-2147483648
        }
    ]
}

分析完 init() 方法之后,我们再来分析 process() 方法。

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (CollectionUtils.isNotEmpty(annotations)) {
        // 1. 获取到所有的@Route注解处理的 Element
        Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
        try {
            // 2. 调用 parseRoutes() 进行处理
            this.parseRoutes(routeElements);

        } catch (Exception e) {
            logger.error(e);
        }
        return true;
    }

    return false;
}

process() 方法可以看到,主要起作用的是 parseRoutes(),这个方法大概几百行,我们还是一部分一部分来分析。

// 获取 android.app.Activity 的 TypeMirror
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
// 获取 android.app.Service 的 TypeMirror
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
// 获取 android.app.Fragment 的 TypeMirror
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
// 获取 android.support.v4.app.Fragment 的 TypeMirror
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();

// 获取 com.alibaba.android.arouter.facade.template.IRouteGroup 的 TypeElement
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
// 获取 com.alibaba.android.arouter.facade.template.IProvider 的 TypeElement
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);

// 获取 RouteMeta 的 ClassName
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
// 获取 RouteType 的 ClassName
ClassName routeTypeCn = ClassName.get(RouteType.class);

这一部分的代码只是做了一些数据获取操作,后面会用到这些数据,我们继续向下分析。

// 构建参数类型 Map<String, RouteMeta>
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
        ClassName.get(Map.class),
        ClassName.get(String.class),
        ClassName.get(RouteMeta.class)
);

// 构建参数 Map<String, Class<? extends IRouteGroup> routes
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot,
        "routes").build();
// 构建参数 Map<String ,RouteMeta> atlas
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup,
        "atlas").build();
// 构建参数 Map<String, RouteMeta> providers
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup,
        "providers").build();  // Ps. its param type same as groupParamSpec!

/**
 * 构建方法
 * @Overrid
 * public void loadInfo(Map<String, Class<? extends IRouteGroup> routes) {
 *
 * }
 */
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(
        METHOD_LOAD_INTO)
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(rootParamSpec);

这一部分代码用 javapoet 定义了一些需要生成的文件里面的方法和参数,为生成最终的文件做准备。我们继续向下分析。

for (Element element : routeElements) {
    // 1. 获取 TypeElement 元素的 TypeMirror 实例,用于判断是Activity,Fragment,Service,IProvider等
    TypeMirror tm = element.asType();
    // 2. 获取 TypeElement 上修饰的 @Route 实例
    Route route = element.getAnnotation(Route.class);
    RouteMeta routeMeta;

    // 3. 判断 TypeElement 的类型;
    if (types.isSubtype(tm, type_Activity)) {
        // 3.1 如果是 Activity ,则构建 Activity 对应的 RouteMeta ,RouteMeta 是存放路由信息的类。
        Map<String, Integer> paramsType = new HashMap<>();
        Map<String, Autowired> injectConfig = new HashMap<>();
        for (Element field : element.getEnclosedElements()) {
            // 遍历当前类的所有元素,找到类型为 FIELD 的 且有 @Autowired注解修改的,并且属性不是IProvider子类,然后存放到对应的 Map 中;
            if (field.getKind().isField() && field.getAnnotation(Autowired.class)
                    != null && !types.isSubtype(field.asType(), iProvider)) {
                // 获取属性上的 @Autowired 实例
                Autowired paramConfig = field.getAnnotation(Autowired.class);
                // 获取注入的key名称
                String injectName = StringUtils.isEmpty(paramConfig.name())
                        ? field.getSimpleName().toString() : paramConfig.name();
                // 存放到 paramsType 里面
                paramsType.put(injectName, typeUtils.typeExchange(field));
                // 存放到 injectConfig 里面
                injectConfig.put(injectName, paramConfig);
            }
        }
        // 创建当前 Activity 的 RouteMeta(存放路由信息的类)
        routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
        routeMeta.setInjectConfig(injectConfig);
    } else if (types.isSubtype(tm, iProvider)) { 
        // 3.2 如果是 IProvider ,则创建 IProvider 的 RouteMeta
        routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
    } else if (types.isSubtype(tm, type_Service)) { 
        // 3.3 如果是 Service , 则创建 Service 的 RouteMeta 
        routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
    } else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
        // 3.4 如果是 Fragment ,则创建 Fragment 的 RouteMeta
        routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
    } else {
        // 3.5 可以看到,@Route 只支持 Activity,IProvider,Service,Fragment
        throw new RuntimeException(
                "ARouter::Compiler >>> Found unsupported class type, type = ["
                        + types.toString() + "].");
    }

    // 4. 调用 categories() 对 RouteMeta 进行分类处理。
    categories(routeMeta);
}

这里通过 for 语句对所有 @Route 修饰的类的 TypeElement 元素进行遍历,然后根据其具体的类型,生成对应的 RouteMeta 对象,存放路由信息。

  1. 获取 TypeElement 元素的 TypeMirror 实例,用于判断是否是 ActivityFragmentServiceIProvider
  2. 获取 TypeElement 上修饰的 @Route 实例;
  3. 判断当前类的具体类型,并生成对应的 RouteMeta 存放路由信息;
  4. 调用 categories()RouteMeta 进行分类。

我们再看看 categories() 方法做了什么。

private void categories(RouteMeta routeMeta) {
    // 1. 校验 routeMeta 里面存放的路径是否是正确的
    if (routeVerify(routeMeta)) {
        Set<RouteMeta> routeMetas = groupMap.get(routeMeta.getGroup());
        // 2. 根据 group 进行分组存放到 groupMap 里面
        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(routeMeta);
            groupMap.put(routeMeta.getGroup(), routeMetaSet);
        } else {
            routeMetas.add(routeMeta);
        }
    }
}

categories() 方法的逻辑很简单,对路由信息进行分类。

  1. 通过 routeVerify()RouteMeta 进行校验;
  2. 按照 group 进行分类,存放到 groupMap 中。

看完 categories() ,我们接着往下看 parseRoutes()

/**
 * @Overrid
 * public void loadInfo(Map<String, RouteMeta> providers) {
 *
 * }
 */
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(
        METHOD_LOAD_INTO)
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(providerParamSpec);

这里也是使用 javapoet 定义了 loadInfo() 方法。

// 这里对 categories() 方法分类后的 groupMap 进行遍历
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
    String groupName = entry.getKey();
/**
 * @Overrid
 * public void loadInfo(Map<String, RouteMeta>> atlas) {
 *
 * }
 */
Spec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(
            METHOD_LOAD_INTO)
            .addAnnotation(Override.class)
            .addModifiers(PUBLIC)
            .addParameter(groupParamSpec);

    List<RouteDoc> routeDocList = new ArrayList<>();

    Set<RouteMeta> groupData = entry.getValue();
    // 遍历当前 group 下面所有的 RouteMeta
    for (RouteMeta routeMeta : groupData) {
        RouteDoc routeDoc = extractDocInfo(routeMeta);

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

        switch (routeMeta.getType()) {
            case PROVIDER:
                // 获取实现的接口 
                List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
                // 遍历接口
                for (TypeMirror tm : interfaces) {
                    routeDoc.addPrototype(tm.toString());
                    if (types.isSameType(tm, iProvider)) { 
                        loadIntoMethodOfProviderBuilder.addStatement(
                                "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                                (routeMeta.getRawType()).toString(),
                                routeMetaCn,
                                routeTypeCn,
                                className,
                                routeMeta.getPath(),
                                routeMeta.getGroup());
                    } else if (types.isSubtype(tm, iProvider)) {
                        loadIntoMethodOfProviderBuilder.addStatement(
                                "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                                tm.toString(), 
                                routeMetaCn,
                                routeTypeCn,
                                className,
                                routeMeta.getPath(),
                                routeMeta.getGroup());
                    }
                }
                break;
            default:
                break;
        }

        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<>();
            
            // 对非IProvider且被@Autowired修饰的域进行处理
            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();

        // 添加路由信息
        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$$groupName 文件
    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);

    rootMap.put(groupName, groupFileName);
    docSource.put(groupName, routeDocList);
}

这里对 categories() 处理后的 groupMap 进行遍历,然后生成对应的 IRouteGroup 文件 ARouter$$Group$$grouName。我们继续往下看。

if (MapUtils.isNotEmpty(rootMap)) {
    // 给 IRouteRoot 的 loadInfo() 添加数据
    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()));
    }
}

// 生成doc文件
if (generateDoc) {
    docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
    docWriter.flush();
    docWriter.close();
}

// 生成 ARouter$$Providers$$moduleName,存放所有注册的IProvider 信息
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);


// 生成 ARouter$$Root$$moduleName ,存放 groupName 和 IRouteGroup 的映射关系
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);

这段代码就比较简单了,使用 javapoet 生成了三个文件。

到此,对 RouteProcessor 的源码我们就分析完了。总结来说,经过 RouteProcessor 的处理,会生成如下几类文件。

  1. ARouter$$Group$$groupName,实现了 IRouteGroup,用来存放当前 groupName 下所有的路由信息;
  2. ARouter$$Root$$moduleName,实现了 IRouteRoot,用来存放当前模块下,groupNameIRouteGroup 的映射关系;
  3. ARouter$$Providers$$moduleName,实现了 IProviderGroup,用来存放当前模块下所有注册的 IProvider 的映射关系;
  4. 根据 generateDoc ,决定是否生成 arouter-map-of-modulename.json 说明文档;

至此,RouteProcessor 的源码就分析完了。

图解

通过上面的分析,我们大致可以画出 @Route 的基本原理。

编译期.png
运行期.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342