源码分析-ARouter(上)

794 阅读26分钟

1、路由框架

1.1、原生路由的缺陷

原生路由方案一般是通过显式intent和隐式intent两种方式实现的,而在显式intent的情况下,因为会存在直接的类依赖的问题,导致耦合非常严重;而在隐式intent情况下,则会出现规则集中式管理,导致协作变得非常困难。而且一般而言配置规则都是在Manifest中的,这就导致了扩展性较差。除此之外,使用原生的路由方案会出现跳转过程无法控制的问题,因为一旦使用了StartActivity()就无法插手其中任何环节了,只能交给系统管理,这就导致了在跳转失败的情况下无法降级,而是会直接抛出运营级的异常。

1.2、路由组件

前面提到的主要是开发与协作中的问题,而使用一款路由框架时还会涉及到其他的两个大方面:一方面是组件化,而另一方面就是Native和H5的问题。刚才所提到的主要是开发和协作中作为开发者所需要面对的问题,而一旦一款APP达到一定体量的时候,业务就会膨胀得比较严重,而开发团队的规模也会越来越大,这时候一般都会提出组件化的概念。组件化就是将APP按照一定的功能和业务拆分成多个小组件,不同的组件由不同的开发小组来负责,这样就可以解决大型APP开发过程中的开发与协作的问题,将这些问题分散到小的APP中。目前而言组件化已经有非常多比较成熟的方案了,而自定义路由框架也可以非常好地解决整个APP完成组件化之后模块之间没有耦合的问题,因为没有耦合时使用原生的路由方案肯定是不可以的。

1.3、路由组件的特点

分发:把一个URL或者请求按照一定的规则分配给一个服务或者页面来处理,这个流程就是分发,分发是路由框架最基本的功能,当然也可以理解成为简单的跳转。

管理:将组件和页面按照一定的规则管理起来,在分发的时候提供搜索、加载、修改等操作,这部分就是管理,也是路由框架的基础,上层功能都是建立在管理之上。

控制:就像路由器一样,路由的过程中,会有限速、屏蔽等一些控制操作,路由框架也需要在路由的过程中,对路由操作做一些定制性的扩展,比方刚才提到的AOP,后期的功能更新,也是围绕这个部分来做的。

2、ARouter的技术方案

image.png 1、最基础的Compiler这个SDK,其内部有三个处理器,分别是:Route Processor,Interceptor Processor以及Autowire Processor,通过名字就可以看出这三个处理器分别是处理路径路由、拦截器和进行自动装配的。

2、在编译过程中,还有一个gradle插件,目的是在Arouter中插入相关注册代码代替在Arouter.Init()时扫描dex文件获取到所有route相关类。

3、API的SDK是用户在运行期使用的,这一部分主要分为四层:

Launcher:这一层是开发者可以直接用到的,其实所有的API都是在这一层中。

facade:从上图中可以看到Frossard层也是绿色的,表示这一层也是可以被外部调用的,Frossard层其实包含了三部分,分别是:Service、Callback和Template,这里的Service概念和服务端的Service概念是相似的,也是在客户端的简单引申,但是却不同于Android组件中的Service,这里的Service是ARouter抽象出来的概念,从本质上讲,这里的Service是接口,从意义上讲是将一定的功能和组件封装成接口,并对外提供能力。Template则是模板,主要用于在编译期执行的SDK,这个SDK会在编译期生成一些映射文件,而这些映射文件会按照Template组件中提供的模板来生成,这样按照一定的规则和约束生成映射文件也方便Route在运行的时候进行读取。

SDK内部实现:这一层包括了Ware House、Thread、Log、Exception以及Class工具。Ware House主要存储了ARouter在运行期间加载的一些配置文件以及映射关系;而Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的;Class工具则是用于解决不同类型APK的兼容问题的。

Logistics Center:从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层,当然也会按照功能模块进行划分。

3、源码分析

主要代码结构

module功能
arouter-api上层主要代码,包括入口类ARouter,主要逻辑代码类LogisticsCenter,相关辅助类ClassUtils等
arouter-annotationARouter中主要支持的annotation(包括Autowired, Route, Interceptor)的定义,以及RouteMeta等基础model bean的定义
arouter-compilerARouter中annotation对应的annotation processor代码(注解处理器:让这些注解代码起作用,Arouter中主要是生成相关代码
arouter-gradle-plugin一个gradle插件,目的是在arouter中插入相关注册代码(代替在Init时扫描dex文件获取到所有route相关类)
module-java示例
module-java-export示例
module-kotlin示例
App示例

3.1、arouter-annotation

@Route路由注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
    String path(); // Path of route
    String group() default ""; //Used to merger routes, the group name MUST BE USE THE COMMON WORDS
    String name() default ""; //Name of route, used to generate javadoc.
    int extras() default Integer.MIN_VALUE; //Extra data, can be set by user.
    int priority() default -1; //The priority of route
}

@Interceptor拦截器注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
    int priority(); //The priority of interceptor, ARouter will be excute them follow the priority.
    String name() default "Default"; //The name of interceptor, may be used to generate javadoc.
}

@Autowired自动装载注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
    String name() default ""; //Mark param's name or service name.
    boolean required() default false; // If required, app will be crash when value is null.Primitive type wont be check!
    String desc() default ""; //Description of the field
}

RouteType路由类型

public enum RouteType {
    ACTIVITY(0, "android.app.Activity"),
    SERVICE(1, "android.app.Service"),
    PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
    CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
    BOARDCAST(-1, ""),
    METHOD(-1, ""),
    FRAGMENT(-1, "android.app.Fragment"),
    UNKNOWN(-1, "Unknown route type");
    int id;
    String className;
    ...
}

TypeKind参数类型

public enum TypeKind { //目标Class的需要注入的参数的参数类型
    // Base type
    BOOLEAN,
    BYTE,
    SHORT,
    INT,
    LONG,
    CHAR,
    FLOAT,
    DOUBLE,

    // Other type
    STRING,
    SERIALIZABLE,
    PARCELABLE,
    OBJECT;
}

RouteMeta路由元信息

public class RouteMeta {            //每一个RouteMeta就是一条路由元信息
    private RouteType type;         // Type of route
    private Element rawType;        // Raw type of route
    private Class<?> destination;   // Destination
    private String path;            // Path of route
    private String group;           // Group of route
    private int priority = -1;      // The smaller the number, the higher the priority
    private int extra;              // Extra data
    private Map<String, Integer> paramsType;  // Param type
    private String name;

    private Map<String, Autowired> injectConfig;  // Cache inject config.

    public RouteMeta() {
    }
}

用于保存一条路由信息。

3.2、arouter-compiler注解处理器

image.png

ARouter注解处理器一共三种:分别为AutowiredProcessor、InterceptorPorcessor和RouteProcessor,对应处理 @Autowired注解、@Interceptor注解和@Router注解

image.png

由注解处理器处理之后,生成的代码主要如下:其中Autowired由注解@Autowired和注解处理器AutowiredProcessor生成,Interceptor由注解@Inpterceptor和InterceptorProcessor生成,Group、Provider、Route均有注解@Route和RouteProcessor生成。

image.png

BaseProcessor:注解处理器的基类

RouteProcessor:路由注解处理器,生成Group、Provider、Root三种类型的代码

AutowiredProcessor:自动装载注解处理器,生成Autowired类型的代码

InterceptorProcessor:拦截器注解处理器,生成Interceptor类型的代码

3.3、arouter-gradle-plugin 插件

ARouter插件在编译过程中,完成代码的插入,以便于业务方在调用ARouter.init()的时候直接获取到被@Route注解的相关的类信息,替代业务方在ARouter.init()时通过扫描dex文件来获取@Route注解的相关的类的信息。

image.png

com.alibaba.aouter.properties
//properties里就一行内容,插件入口为PluginLaunch
implementation-class=com.alibaba.android.arouter.register.launch.PluginLaunch

ScanSetting:需要扫描的基础信息

PluginLaunch:插件的入口

RegisterTransform:插件具体执行逻辑的地方

3.4、arouter-api

WareHouse:数据存储的仓库

/**
 * Storage of route meta and other data.
 */
class Warehouse {
    // Cache route and metas
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); //init 初始化之后扫出来的东西放在这里
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}

3、ARouter使用流程分析

Apk的打包编译流程

image.png

如上图所示,我们可以在步骤1和步骤2处对代码进行改造:

1、在步骤1处,由.java文件生成.class文件时,APT、AndroidAnnotation等就是在此处触发代码生成。 2、在步骤2处,有.class文件进一步优化生成.dex文件时,也就是直接操作字节码文件,就是字节码插桩。

image.png

路由注解部分

在分享路由注解部分时,会讲到两部分的内容,一部分是跟注解处理器相关的代码,一部分是跟代码插入相关的代码

路由注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
    String path();//Path of route
    String group() default "";//Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!!
    String name() default "";//Name of route, used to generate javadoc.
    int extras() default Integer.MIN_VALUE;//Extra data, can be set by user.
    int priority() default -1;//The priority of route.
}

路由注解处理器

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.


@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (CollectionUtils.isNotEmpty(annotations)) {
        Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);//拿到被Route注解了的Element
        try {
            logger.info(">>> Found routes, start... <<<");
            this.parseRoutes(routeElements);

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

    return false;
}

//将上面拿到的Elment传给parseRoutes处理
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);

        /*
          构建 $$Rout$$ loadInto()的形参类型

           ```Map<String, Class<? extends IRouteGroup>>```
         */
        ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ParameterizedTypeName.get(
                        ClassName.get(Class.class),
                        WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
                )
        );

        /*
          构建 $$Group$$ 和 $$provider$$ loadInto()的形参类型
          ```Map<String, RouteMeta>``` 
         */
        ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(RouteMeta.class)
        );

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

        //构建方法 loadInto():
        //@Override
        //public void loadInto(Map<String, Class<? extends IRouteGroup>> routers)
        MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(rootParamSpec);

        //  Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
        //遍历所有被@Route注解的element
        for (Element element : routeElements) {
            TypeMirror tm = element.asType();
            Route route = element.getAnnotation(Route.class);
            RouteMeta routeMeta;

            // 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<>();
                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() + "].");
            }
            //对路由节点进行分组,按照GroupName作为key,对应的GroupName相同的RouteMeta构成的Set作为value,数据放在groupMap里面
            categories(routeMeta);
        }

        //构建方法 ARouter$$Providers$$app loadInto():
        //@Override
        //public void loadInto(Map<String, RouteMeta> providers)
        
        MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(providerParamSpec);

        Map<String, List<RouteDoc>> docSource = new HashMap<>();

        // Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
        for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
            String groupName = entry.getKey();


            //构建方法 ARouter$$Group$$ loadInto():
            //@Override
            //public void loadInto(Map<String, RouteMeta> atlas)
            MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(groupParamSpec);

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

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

                ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
                
                //针对provider有单独的处理,loadIntoMethodOfProviderBuilder添加了对应的代码块
                switch (routeMeta.getType()) {
                    case PROVIDER:  // Need cache provider's super class
                        List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
                        for (TypeMirror tm : interfaces) {
                            routeDoc.addPrototype(tm.toString());

                            if (types.isSameType(tm, iProvider)) {   // Its implements iProvider interface himself.
                                // This interface extend the IProvider, so it can be used for mark provider
                                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)) {
                                // This interface extend the IProvider, so it can be used for mark provider
                                loadIntoMethodOfProviderBuilder.addStatement(
                                        "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                                        tm.toString(),    // So stupid, will duplicate only save class name.
                                        routeMetaCn,
                                        routeTypeCn,
                                        className,
                                        routeMeta.getPath(),
                                        routeMeta.getGroup());
                            }
                        }
                        break;
                    default:
                        break;
                }

                // Make map body for paramsType
                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();

                //$$Group$$ 的loadInto里,添加对应的代码(在内层的routeMeta循环里)
                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);
            }

            // 按照GroupName的形式生成对应的ARouter$$Group$$groupName的文件(在外层的entry循环里),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 + "<<<");
            //将groupName和生成的GroupFileName存起来,用于生成ARouter$$Root$$xxx文件
            rootMap.put(groupName, groupFileName);
            docSource.put(groupName, routeDocList);
        }

        //在rootMap有数据的情况下,往loadIntoMethodOfRootBuilder也就是Arouter$$Root$$Name的loadInto里添加代码
        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()));
            }
        }

        // Output route doc
        if (generateDoc) {
            docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
            docWriter.flush();
            docWriter.close();
        }

        // 生成ARouter$$Providers$$moduleName文件
        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);

        logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<");

        // 生成ARouter$$Root$$moduleName文件
        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);

        logger.info(">>> Generated root, name is " + rootFileName + " <<<");
    }
}

RouteProcess流程里面,一共做了以下几件事:

1、生成ARouter$$Group$$GroupName文件,生成ARouter$$Provider$$ModuleName文件,生成ARouter$$Root$$ModuleName文件

2、对于Provider类型的,同时存在与Group和Provider文件里,但是存储的形式不同

3、Root里面存放的是Group信息,Group里面存放的才是RouteMeta信息

image.png

生成的Group类示例代码


/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
    atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
    atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
    atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
    atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
    atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
  }
}
public class ARouter$$Group$$yourservicegroupname implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/yourservicegroupname/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
    atlas.put("/yourservicegroupname/json", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
    atlas.put("/yourservicegroupname/single", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
  }
}

生成的Provider类示例代码

public class ARouter$$Providers$$modulejava implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
  }
}

生成的Root类示例代码

public class ARouter$$Root$$modulejava implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("m2", ARouter$$Group$$m2.class);
    routes.put("module", ARouter$$Group$$module.class);
    routes.put("test", ARouter$$Group$$test.class);
    routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
  }
}

代码插装部分

ScanSetting:扫描信息

class ScanSetting {
    static final String PLUGIN_NAME = "com.alibaba.arouter"
    /**
     * The register code is generated into this class
     */
    static final String GENERATE_TO_CLASS_NAME = 'com/alibaba/android/arouter/core/LogisticsCenter'
    static final String GENERATE_TO_CLASS_FILE_NAME = GENERATE_TO_CLASS_NAME + '.class' //需要插入代码的类
    static final String GENERATE_TO_METHOD_NAME = 'loadRouterMap'//需要被插入代码的方法

    static final String ROUTER_CLASS_PACKAGE_NAME = 'com/alibaba/android/arouter/routes/'
    private static final INTERFACE_PACKAGE_NAME = 'com/alibaba/android/arouter/facade/template/'

    static final String REGISTER_METHOD_NAME = 'register'//需要插入代码的方法
    
    String interfaceName = ''

    File fileContainsInitClass
  
    ArrayList<String> classList = new ArrayList<>() //存放扫出来的类的list

    ScanSetting(String interfaceName){
        this.interfaceName = INTERFACE_PACKAGE_NAME + interfaceName
    }

}

PluginLaunch:插入件的入口

PluginLaunch.groovy

public class PluginLaunch implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        def isApp = project.plugins.hasPlugin(AppPlugin)
        //only application module needs this plugin to generate register code
        if (isApp) {
            Logger.make(project)

            Logger.i('Project enable arouter-register plugin')

            def android = project.extensions.getByType(AppExtension)
            def transformImpl = new RegisterTransform(project)

            //init arouter-auto-register settings
            ArrayList<ScanSetting> list = new ArrayList<>(3)
            list.add(new ScanSetting('IRouteRoot'))
            list.add(new ScanSetting('IInterceptorGroup'))
            list.add(new ScanSetting('IProviderGroup'))
            RegisterTransform.registerList = list
            //register this plugin
            android.registerTransform(transformImpl)
        }
    }

}

1、PluginLaunch里面初始化了一个list,里面放入了三个接口 IRouteRoot、IIterceptorGroup、 IproviderGroup 类型,后续会扫出这三个接口的实现类,在前面的注解处理器里被@Route注解生成的三种类型的代码以及后面的被@Interceptor注解生成的代码,就是继承自这三种类型。

2、注册了一个Transform,代码的实际插入在RegisterTransform里面。

Google官方在Android GradleV1.5.0版本以后提供了Transform API,它允许第三方插件在编译后的类文件转换为dex文件之前做处理操作,我们需要做的就是实现Transform来对.class文件便遍历来拿到所有方法,修改完成后再对源文件进行替换就可以了。

/**
 * transform api
 * <p>
 *     1. Scan all classes to find which classes implement the specified interface
 *     2. Generate register code into class file: {@link ScanSetting#GENERATE_TO_CLASS_FILE_NAME}
 * @author billy.qi email: qiyilike@163.com
 * @since 17/3/21 11:48
 */
class RegisterTransform extends Transform {

    Project project
    static ArrayList<ScanSetting> registerList
    static File fileContainsInitClass;

    RegisterTransform(Project project) {
        this.project = project
    }

    // 返回对应的 Transform Task的名称。
    @Override
    String getName() {
        return ScanSetting.PLUGIN_NAME
    }

    // 输入文件的类型:可供我们去处理的有两种类型, 分别是编译后的java代码, 以及资源文件(非res下文件, 而是assests内的资源)
    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS
    }

    // 指定插件的试用范围:这个插件会扫描工程里所有的Classes
    @Override
    Set<QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }

    // 是否支持增量:如果支持增量执行, 则变化输入内容可能包含 修改/删除/添加 文件的列表
    @Override
    boolean isIncremental() {
        return false
    }

    // Transform的执行主函数,进行具体的转换过程
    @Override
    void transform(Context context, Collection<TransformInput> inputs
                   , Collection<TransformInput> referencedInputs
                   , TransformOutputProvider outputProvider
                   , boolean isIncremental) throws IOException, TransformException, InterruptedException {

        Logger.i('Start scan register info in jar file.')

        long startTime = System.currentTimeMillis()
        boolean leftSlash = File.separator == '/'

        if (!isIncremental){
            outputProvider.deleteAll()
        }

        inputs.each { TransformInput input ->
            //通过AMS 的 ClassVisistor 扫描所有的jar 文件,将所有扫描到的IRouteRoot IInterceptorGroup IInterceptorGroup类都加到ScanSetting 的 classList中
        
            //如果jar包是 LogisticsCenter.class,标记该类文件到 fileContainsInitClass

            // scan all jars
            input.jarInputs.each { JarInput jarInput ->
                String destName = jarInput.name
                // rename jar files
                def hexName = DigestUtils.md5Hex(jarInput.file.absolutePath)
                if (destName.endsWith(".jar")) {
                    destName = destName.substring(0, destName.length() - 4)
                }
                // input file
                File src = jarInput.file
                // output file
                File dest = outputProvider.getContentLocation(destName + "_" + hexName, jarInput.contentTypes, jarInput.scopes, Format.JAR)

                //scan jar file to find classes
                if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {
                    ScanUtil.scanJar(src, dest)
                }
                FileUtils.copyFile(src, dest)

            }
            // scan class files
            input.directoryInputs.each { DirectoryInput directoryInput ->
                File dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
                String root = directoryInput.file.absolutePath
                if (!root.endsWith(File.separator))
                    root += File.separator
                directoryInput.file.eachFileRecurse { File file ->
                    def path = file.absolutePath.replace(root, '')
                    if (!leftSlash) {
                        path = path.replaceAll("\\", "/")
                    }
                    if(file.isFile() && ScanUtil.shouldProcessClass(path)){
                        ScanUtil.scanClass(file)
                    }
                }

                // copy to dest
                FileUtils.copyDirectory(directoryInput.file, dest)
            }
        }

        Logger.i('Scan finish, current cost time ' + (System.currentTimeMillis() - startTime) + "ms")

        if (fileContainsInitClass) {
            registerList.each { ext ->
                Logger.i('Insert register code to file ' + fileContainsInitClass.absolutePath)

                if (ext.classList.isEmpty()) {
                    Logger.e("No class implements found for interface:" + ext.interfaceName)
                } else {
                    ext.classList.each {
                        Logger.i(it)
                    }
                    RegisterCodeGenerator.insertInitCodeTo(ext)
                }
            }
        }

        Logger.i("Generate code finish, current cost time: " + (System.currentTimeMillis() - startTime) + "ms")
    }
}
ScanUtil.groovy


static void scanJar(File jarFile, File destFile) {
    if (jarFile) {
        def file = new JarFile(jarFile)
        Enumeration enumeration = file.entries()
        while (enumeration.hasMoreElements()) {
            JarEntry jarEntry = (JarEntry) enumeration.nextElement()
            String entryName = jarEntry.getName()
            //com/alibaba/android/arouter/routes/xxx是否有这种路径的文件
            if (entryName.startsWith(ScanSetting.ROUTER_CLASS_PACKAGE_NAME)) {
                InputStream inputStream = file.getInputStream(jarEntry)
                scanClass(inputStream)
                inputStream.close()
            } else if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {
                //是否有com/alibaba/android/arouter/core/LogisticsCenter.java文件
                // mark this jar file contains LogisticsCenter.class
                // After the scan is complete, we will generate register code into this file
                RegisterTransform.fileContainsInitClass = destFile
            }
        }
        file.close()
    }
}

1、通过ASM扫描了对应的jar 文件和class文件,并将扫描到的对应routes包下的类加入到ScanSetting 的classList属性 中

2、如果扫描到包含LogisticsCenter.class 类文件,将该文件记录到fileContainsInitClass 字段中。

3、扫描完成的文件重命名。

4、最后通过RegisterCodeGenerator.insertInitCodeTo(ext) 方法插入初始化代码到LogisticsCenter.class中。

在整个编译的过程中,一共完成了两件事情:

1、生成被@Route、@Interceptors、@Autewired注解的类的相关的辅助类

2、往LogisticsCenter.loadRouterMap()方法里插入第一步生成的类的信息。

ARouter初始化

image.png

在ARouter的init()流程中,一共完成了两件时间:

1、Warehouse.groupsIndex,Warehouse.providersIndex,Warehouse.interceptorsIndex被放入了数据

2、初始化了一个被@Route(path = "/arouter/service/interceptor")注解的InterceptorServiceImpl,并调用了其init()。

ARouter.java

/**
 * Init, it must be call before used router.
 */
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.java

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

//在afterInit里面,调用了InterceptorService

static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

LogisticsCenter.java

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context; //静态持有的Application上下文
    executor = tpe; //静态持有的线程池

    try {
        long startInit = System.currentTimeMillis();
        //load by plugin first
        loadRouterMap(); //这个方法把registerByPlugin设置为false,做了优化
        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)) { //如果是Debug版本或者新版本,会直接重新加载所有Router
                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                // These class was generated by arouter-compiler. 通过指定包名com.alibaba.android.arouter.routes找到编译期产生的routers目录下的类名(不包含装载类)
                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 { //如果不是debug版本和新版本,则直接从Sp里取出routerMap
                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() + "]");
    }
}

在LogisticsCenter.init()函数里面:首次调用时,会你用ClassUtils.getFileNameByPackageName()来扫描对应的包下面的所有的className,在首次扫描完成之后会存储在Sp里面,这样后面不需要再次扫描。

ClassUtils.java
/**
 * 通过指定包名,扫描包下面包含的所有的ClassName
 *
 * @param context     U know
 * @param packageName 包名
 * @return 所有class的 集合
 */
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);
                        }
                    }
                } catch (Throwable ignore) {
                    Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                } finally {
                    if (null != dexfile) {
                        try {
                            dexfile.close();
                        } catch (Throwable ignore) {
                        }
                    }

                    parserCtl.countDown();
                }
            }
        });
    }

    parserCtl.await();

    Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
    return classNames;
}

启动优化

LogisticsCenter.java

//arouter-auto-register 插件将在此方法中生成代码调用此方法来注册所有路由器、拦截器和提供程序
/**
 * arouter-auto-register plugin will generate code inside this method
 * call this method to register all Routers, Interceptors and Providers
 */
private static void loadRouterMap() {
    registerByPlugin = false;
    // auto generate register code by gradle plugin: arouter-auto-register
    // looks like below:
    // registerRouteRoot(new ARouter..Root..modulejava());
    // registerRouteRoot(new ARouter..Root..modulekotlin());
}

loadRouterMap()函数里利用了字节码插桩来优化ARouter的首次启动耗时,在初始化时,会在扫描之前,判断registerByPlugin,如果我们需要的map已经被插件注册了,那也就不需要进行下面的耗时操作了但是我们可以看到在loadRouterMap中,registerByPlugin一直被设为false

LogisticsCenter.java

//源码代码,插桩前 
private static void loadRouterMap() { 
    //registerByPlugin一直被置为false 
    registerByPlugin = false; 
} 

//插桩后反编译代码 
private static void loadRouterMap() { 
    registerByPlugin = false;
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava"); 
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin"); 
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi"); 
    register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava"); 
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava"); 
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin"); 
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi"); 
}


//字节码形式,可以看到一共插入了7个register函数
.method private static loadRouterMap()V
    .registers 2

    .line 64
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v1, "loadRouterMap"

    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 65
    const/4 v0, 0x0

    sput-boolean v0, Lcom/alibaba/android/arouter/core/LogisticsCenter;->registerByPlugin:Z

    .line 71
    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    const-string v0, "com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi"

    invoke-static {v0}, Lcom/alibaba/android/arouter/core/LogisticsCenter;->register(Ljava/lang/String;)V

    return-void
.end method

image.png

image.png

image.png

LogisticsCenter.java

/**
 * register by class name
 * Sacrificing a bit of efficiency to solve
 * the problem that the main dex file size is too large
 */
private static void register(String className) {
    logger.info(TAG, "register before use");
    if (!TextUtils.isEmpty(className)) {
        try {
            Class<?> clazz = Class.forName(className);
            Object obj = clazz.getConstructor().newInstance();
            if (obj instanceof IRouteRoot) {
                registerRouteRoot((IRouteRoot) obj);
            } else if (obj instanceof IProviderGroup) {
                registerProvider((IProviderGroup) obj);
            } else if (obj instanceof IInterceptorGroup) {
                registerInterceptor((IInterceptorGroup) obj);
            } else {
                logger.info(TAG, "register failed, class name: " + className
                        + " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
            }
        } catch (Exception e) {
            logger.error(TAG,"register class error:" + className, e);
        }
    }
}

/**
 * method for arouter-auto-register plugin to register Routers
 * @param routeRoot IRouteRoot implementation class in the package: com.alibaba.android.arouter.core.routers
 */
private static void registerRouteRoot(IRouteRoot routeRoot) {
    markRegisteredByPlugin();
    if (routeRoot != null) {
        routeRoot.loadInto(Warehouse.groupsIndex);
    }
}

/**
 * method for arouter-auto-register plugin to register Providers
 * @param providerGroup IProviderGroup implementation class in the package: com.alibaba.android.arouter.core.routers
 */
private static void registerProvider(IProviderGroup providerGroup) {
    markRegisteredByPlugin();
    if (providerGroup != null) {
        providerGroup.loadInto(Warehouse.providersIndex);
    }
}

/**
 * method for arouter-auto-register plugin to register Interceptors
 * @param interceptorGroup IInterceptorGroup implementation class in the package: com.alibaba.android.arouter.core.routers
 */
private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
    markRegisteredByPlugin();
    if (interceptorGroup != null) {
        interceptorGroup.loadInto(Warehouse.interceptorsIndex);
    }
}


/**
 * mark already registered by arouter-auto-register plugin
 */
private static void markRegisteredByPlugin() {
    if (!registerByPlugin) {
        registerByPlugin = true;
    }
}

初始化工作全部完成,其中仓库Warehouse缓存了全局应用的信息,找到三种对应的文件,然后调用其文件下的loadInto方法,把数据添加到下面几个xxx_Index变量中:

Warehouse.groupsIndex:存放的Root信息,每个Root信息里面存放的是当前module下的Group信息,Group下的信息才是每一个被@Route注解的RouteMeta信息

register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin"); register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");

Warehouse.interceptorsIndex:存放的拦截器的信息

register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");

Warehouse.providersIndex:存放的provider的信息

register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");

ARouter调用流程

image.png

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


public static ARouter getInstance() {
    if (!hasInit) { //如果没有初始化过,则直接抛出异常,所以使用ARouter之后,需要调用ARouter.init()
        throw new InitException("ARouter::Init::Invoke init(context) first!");
    } else {
        if (instance == null) {
            synchronized (ARouter.class) {
                if (instance == null) {
                    instance = new ARouter();
                }
            }
        }
        return instance;
    }
}

public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}

_ARouter.java
/**
 * Build postcard by path and default group
 */
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,根据path生产的Group,三个参数分别是 /test/activity2, test, true
    }
}

//根据path拿到group
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)); ///拿到传入的path /test/activity2 /前的内容test作为group
        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;
    }
}

//new一个PostCard出来,这里有一个PathReplaceService是个接口,没有实现,业务方可以根据时候需要对path进行二次处理来决定是否需要实现一个PathReplaceService,但是在这个地方没用
protected Postcard build(String path, String group, Boolean afterReplace) {
    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        if (!afterReplace) { //afterReplace == true,所以不走这个逻辑
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
        }
        return new Postcard(path, group);
    }
}

public interface PathReplaceService extends IProvider {

    String forString(String path);

    Uri forUri(Uri uri);
}


ARouter.getInstance().build("/test/activity2"),在build完成之后,拿到了一个由参数 /test/activity2 和 test 实例化的PostCard

PostCard.java

public Postcard(String path, String group) {
    this(path, group, null, null);
}

//开始navigation
public Object navigation() {
    return navigation(null);
}

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

//继续navigation
public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback); //context是null, this是new出来的PostCard,requestCode是-1,callback是null
}


Postcard里面的navigation最终还是调用到_ARouter里面的navigation,传入的context和callback均为null。

ARouter.java

/**
 * Launch the navigation.
 *
 * @param mContext    null
 * @param postcard    实例化的对象
 * @param requestCode Set for startActivityForResult -1
 * @param callback    null
 */
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
    return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
_Aouter.java

/**
 * Use router navigation.
 *
 * @param context     Activity or null.
 * @param postcard    Route metas
 * @param requestCode RequestCode
 * @param callback    cb
 */
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); //传进来的context为null,mContext为ARouter.init的时候传入的Application

    try {
        LogisticsCenter.completion(postcard); //补全postCard,现在的postCard里面只有path 和 group
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());

        if (debuggable()) {
            // Show friendly tips for user.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
            });
        }

        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;
    }
    
    
    //补全后的postCard执行下面的逻辑,call为null

    if (null != callback) {
        callback.onFound(postcard);
    }


    //是否是绿色通道,及是否需要进行拦截器部分的操作,拦截器部分执行完成之后的回调为onContinue,回调里执行_navigation,如果是绿色通道,及不需要执行拦截器操作的时候,直接执行_navtigation
    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(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(postcard, requestCode, callback);
    }

    return null;
}

在_ARouter.navigation的LogisticsCenter.completion(postcard) 对Postcard进行补全。

LogisticsCenter.java

/**
 * Completion the postcard by route metas
 *
 * @param postcard Incomplete postcard, should complete by this method.
 */
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) { //首次进来是Warehouse.routers里面为空,获取到的routeMete为null
        // Maybe its does't exist, or didn't load.
        if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) { //init里面的groupsIdex里面没有包含要跳转的postCard里面的group直接抛出异常
            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()));
                }

                addRouteGroupDynamic(postcard.getGroup(), null); //将这个postCard的group添加到WareHouse.

                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);   // 重新调用此方法
        }
    } else {
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        Uri rawUri = postcard.getUri(); //示例这里的uri为null,type为Activity,下面的代码逻辑都不会走
        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);
                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) {
                        logger.error(TAG, "Init provider failed!", e);
                        throw new HandlerException("Init provider failed!");
                    }
                }
                postcard.setProvider(instance);
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            case FRAGMENT:
                postcard.greenChannel();    // Fragment needn't interceptors
            default:
                break;
        }
    }
}


//动态加载Group里面的内容
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //根据GroupName从Warehouse.groupIndex里面,拿到Group的类名
    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.routes里面
        Warehouse.groupsIndex.remove(groupName);
    }

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

上诉代码以/test/activity2为例,在addRouteGroupDynamic()里面,我们传入的groupName为test。

1、通过test在Warehouse.groupIndex.get(test)拿到这个通过注解生成的类

2、例化之后调用loadInfo方法,把Warehouse.routes传入,相当于往Warehouse.routes里面添加数据

image.png

3、完成之后Warehouse.routes里面的数据如下,添加数据完成之后,再次调用completion函数,再从Warehose.routes里获取RouteMeta

public void loadInto(Map<String, RouteMeta> atlas) {
  atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
  atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
  atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
  atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
  atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
  atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());//根据path拿到对应的RouteMeta

RouteMeta{type=ACTIVITY, rawType=null, destination=class com.alibaba.android.arouter.demo.module1.testactivity.Test2Activity, path='/test/activity2', group='test', priority=-1, extra=-2147483648, paramsType={key1=8}, name='null'}

4、重新调用completion(postcard)

5、执行_navigation()

_Aouter.java
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);
                }
            });

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


private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {
    if (requestCode >= 0) {  // Need start for result
        if (currentContext instanceof Activity) {
            ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
        } else {
            logger.warning(Consts.TAG, "Must use [navigation(activity, ...)] to support [startActivityForResult]");
        }
    } 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);
    }
}


拦截器部分

拦截器注册

拦截器的使用和@Route的使用差不多,只是试用了另一个注解@Interceptor,有两个变量,priority是拦截器的优先级,值越小优先级越高,会优先拦截,name是拦截器的名称。

拦截器的注解

Interceptor.java

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
    /**
     * The priority of interceptor, ARouter will be excute them follow the priority.
     */
    int priority();

    /**
     * The name of interceptor, may be used to generate javadoc.
     */
    String name() default "Default";
}

拦截器的例子:拦截器都实现了Interceptor的process接口,在process函数里对传入的postcard进行拦截操作。

@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {

    @Override
    public void process(final Postcard postcard, final InterceptorCallback callback) {
      
    }

    @Override
    public void init(Context context) {
        Log.e("testService", Test1Interceptor.class.getName() + " has init.");
    }
}

@Interceptor(priority = 90)
public class TestInterceptor90 implements IInterceptor {

    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        callback.onContinue(postcard);
    }

    @Override
    public void init(Context context) {
        Log.e("test", "位于moudle1中的拦截器初始化了");
    }
}
public interface IInterceptor extends IProvider {

    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
}
public interface IInterceptor extends IProvider {

    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
}

拦截器因为是自动注册的,所以可以将不同功能的拦截器放在不同功能的模块中,只有模块被打包到整个项目中,因为自动注册机制所以拦截器就会生效,如果不将这些拦截器放到模块并打包到项目中,那就不会生效,这样就不用去做很多注册与反注册的工作,这也是ARouter适用于模块开发的原因之一。

拦截器的注解处理器

@AutoService(Processor.class)
@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)
public class InterceptorProcessor extends BaseProcessor {
    private Map<Integer, Element> interceptors = new TreeMap<>();
    private TypeMirror iInterceptor = null;
    //初始化,做一些你想做的操作
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        iInterceptor = elementUtils.getTypeElement(Consts.IINTERCEPTOR).asType();
    }

    //process部分,扫描被@Interceptor注解的类,生成代码
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (CollectionUtils.isNotEmpty(annotations)) {
            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
            try {
                parseInterceptors(elements);
            } catch (Exception e) {
                logger.error(e);
            }
            return true;
        }
        return false;
    }

    //扫出来的element交给parseInterceptors处理
    private void parseInterceptors(Set<? extends Element> elements) throws IOException {
        if (CollectionUtils.isNotEmpty(elements)) {

            // 校验和缓存数据
            for (Element element : elements) {
                if (verify(element)) {  // Check the interceptor meta
                    logger.info("A interceptor verify over, its " + element.asType());
                    Interceptor interceptor = element.getAnnotation(Interceptor.class);
                    //查看是否有相同优先级的拦截器,如果有,抛出异常
                    Element lastInterceptor = interceptors.get(interceptor.priority());
                    if (null != lastInterceptor) { // Added, throw exceptions
                        throw new IllegalArgumentException(
                                String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",
                                        interceptor.priority(),
                                        lastInterceptor.getSimpleName(),
                                        element.getSimpleName())
                        );
                    }
                    //缓存对应的优先级和element
                    interceptors.put(interceptor.priority(), element);
                } else {
                    logger.error("A interceptor verify failed, its " + element.asType());
                }
            }

            // Interface of ARouter.
            TypeElement type_IInterceptor = elementUtils.getTypeElement(IINTERCEPTOR);
            TypeElement type_IInterceptorGroup = elementUtils.getTypeElement(IINTERCEPTOR_GROUP);

            /**
             *  生成形参的类型
             *  ```Map<Integer, Class<? extends IInterceptor>>```
             */
            ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
                    ClassName.get(Map.class),
                    ClassName.get(Integer.class),
                    ParameterizedTypeName.get(
                            ClassName.get(Class.class),
                            WildcardTypeName.subtypeOf(ClassName.get(type_IInterceptor))
                    )
            );

             /**
             *  生成的形参
             *  ```Map<Integer, Class<? extends IInterceptor>>  interceptors```
             */
            ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();
             /**
             *  生成的方法:loadInto
             */
            MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(tollgateParamSpec);

            // 遍历interceptors里面的数据生成loadInto里面的代码,key和value对应的就是优先级和被注解的类
            if (null != interceptors && interceptors.size() > 0) {
                // Build method body
                for (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {
                    loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));
                }
            }

            // 生成对应的类
            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                    TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)
                            .addModifiers(PUBLIC)
                            .addJavadoc(WARNING_TIPS)
                            .addMethod(loadIntoMethodOfTollgateBuilder.build())
                            .addSuperinterface(ClassName.get(type_IInterceptorGroup))
                            .build()
            ).build().writeTo(mFiler);

            logger.info(">>> Interceptor group write over. <<<");
        }
    }

    /**
     * Verify inteceptor meta
     *
     * @param element Interceptor taw type
     * @return verify result
     */
    private boolean verify(Element element) {
        Interceptor interceptor = element.getAnnotation(Interceptor.class);
        // It must be implement the interface IInterceptor and marked with annotation Interceptor.
        return null != interceptor && ((TypeElement) element).getInterfaces().contains(iInterceptor);
    }
}

生成的代码如下

public class ARouter$$Interceptors$$modulejava implements IInterceptorGroup {
  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    interceptors.put(7, Test1Interceptor.class);
    interceptors.put(90, TestInterceptor90.class);
  }
}

拦截器初始化

拦截器的初始化,在Arouter.init的时候实现,init函数里除了上文分析的_ARouter.init()部分的内容,在init结束之后,还有一个_Arouter.afterInit()函数的调用。

Arouter.java

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.java


private static InterceptorService interceptorService;//初始化了一个interceptorSerive,后续的拦截器的操作,都是通过这个service实现的

static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

在afterInit函数里面,启动了一个被@Route注解的Service,实现了InterceptorService的doInterceptions()接口。

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {}
public interface InterceptorService extends IProvider {
    //拦截操作的接口
    void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
public interface IProvider {
    void init(Context context);
}
public Object navigation() {
    return navigation(null);
}

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


//因为上面见过这个navigation了,所以这里简化一下,关注我们需要的部分
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {

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

    try {
        LogisticsCenter.completion(postcard); //补全这个postCard
    } catch (NoRouteFoundException ex) {
    }
    Log.e(Consts.TAG, "navigation postcard after completion  : " + postcard.toString());
    if (null != callback) {
        callback.onFound(postcard);
    }

    if (!postcard.isGreenChannel()) { 
      
    } else {
        Log.e(Consts.TAG, "navigation postcard.isGreenChannel()");
        return _navigation(postcard, requestCode, callback);
    }

    return null;
}



//这次的completion里面,如果类型为provider会有特殊的操作
public synchronized static void completion(Postcard postcard) {

    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());

    if (null == routeMeta) {
        addRouteGroupDynamic(postcard.getGroup(), null);
        completion(postcard)
    } else {
        Log.e(Consts.TAG, "completion postcard routeMeta : " + routeMeta.toString());
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

     
        //针对如果是Provider类型,1、调用init函数 2、Warehouse.provider里面存入这个provider 3、postcard里面setProvider 4、设置greenChannel
        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);
                if (null == instance) { // There's no instance of this provider
                    IProvider provider;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        Log.e(Consts.TAG, "Aroute init provider : " + provider.getClass().getName());
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                      
                }
                postcard.setProvider(instance);
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            default:
                break;
        }
    }
}




//返回在completion 里面setProvider的值给interceptorService,这个provider,也就是实例化了一个
InterceptorServiceImpl赋值给interceptorService

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = postcard.getContext();
    System.out.println("_navigation getType : " + postcard.getType());
    switch (postcard.getType()) {
        case ACTIVITY:
         
        case PROVIDER:
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
        case METHOD:
        case SERVICE:
        default:
            return null;
    }

    return null;
}


一顿navigation操作,补全代码操作之后,找到了在ARouter$$Group$$arouter里面的/arouter/service/interceptor的对应的RouteMeta信息,completion里调用了provider的init()及调用了InterceptorServiceImpl的init()函数

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

    //遍历被注册到Warehouse.interceptorsIndex里面的拦截器,调用被遍历到的拦截器的init函数,并把对应的拦截器添加到Warehouse.interceptors里界面,再把标志位interceptorHasInit设置为true
    @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);//拦截器被取出来放到了interceptors里面
                        } 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();
                    }
                }
            }
        });
    }
}

到这里为止,在afterInit()里面的,关于拦截器的初始化就执行完毕了,并没有直接的调用被注册的拦截器的init函数,而是通过了一个被@Route注解的InterceptorServiceImpl来间接调用到的。

拦截器的拦截操作

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;
    }
    System.out.println("navigation" );
    // Set context to postcard.
    postcard.setContext(null == context ? mContext : context);

    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {}
       
    //一般情况下,postCard是没有设置GreenChannel的,所以他们都会经过拦截器的拦截,这个interceptorService就是在afterInit里面被赋值的变量,也就是InterceptorServiceImpl

   //所以这个地方调用了InterceptorServiceImpl.doInterceptions函数
    if (!postcard.isGreenChannel()) {
     
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
   
            @Override
            public void onContinue(Postcard postcard) {
       
                _navigation(postcard, requestCode, callback);
            }

            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }
        });
    } else {
     
        return _navigation(postcard, requestCode, callback);
    }

    return null;
}
@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 (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {

            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 {
                        _execute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);//300S超时
                        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((Throwable) postcard.getTag());
                        } else {
                            callback.onContinue(postcard); //拦截器执行完毕之后,在_Arouter里面,执行continue回调,继续完成后面的_navigation操作
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
            //这里的这个callback是_ARouter里面的传进来的callback
            callback.onContinue(postcard);
        }
    }


    //从Warehouse.interceptors里面有序的取出拦截器,执行拦截操作
    private static void _execute(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() { //取出来的拦截器,对这个postCard执行process进行拦截操作,完成之后,回调onCotinue执行下一个拦截器,直到执行完毕,或者超时
                @Override
                public void onContinue(Postcard postcard) {
                    counter.countDown();
                    _execute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
                }

                @Override
                public void onInterrupt(Throwable exception) {
               
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception);   
                    counter.cancel();

                }
            });
        }
    }

    @Override
    public void init(final Context context) {
        interceptorHasInit = true;        
    }

    //检查是否完成了初始化,有加锁的操作,如果没有完成,会等待10S,这个标志位是在init里面设为true的
    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() + "]");
                }
            }
        }
    }
}

一个具体的拦截器,通过postcard里的path来确认是否来拦截对应的postcard,然后对这个postcard做需要做的操作,完成之后回调onContinue继续下一个拦截器。

@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {

    @Override
    public void process(final Postcard postcard, final InterceptorCallback callback) {
        if ("/test/activity4".equals(postcard.getPath())) {

            // 这里的弹窗仅做举例,代码写法不具有可参考价值
            final AlertDialog.Builder ab = new AlertDialog.Builder(postcard.getContext());
            ab.setCancelable(false);
            ab.setTitle("温馨提醒");
            ab.setMessage("想要跳转到Test4Activity么?(触发了"/inter/test1"拦截器,拦截了本次跳转)");
            ab.setNegativeButton("继续", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    callback.onContinue(postcard);
                }
            });
            ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    callback.onInterrupt(null);
                }
            });
            ab.setPositiveButton("加点料", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    postcard.withString("extra", "我是在拦截器中附加的参数");
                    callback.onContinue(postcard);
                }
            });

            MainLooper.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ab.create().show();
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }


关于拦截器的拦截操作就执行完毕了,并没有直接的调用被注册的拦截器的process函数,而是通过了一个被@Route注解的InterceptorServiceImpl来整体管理拦截器和间接调用到的。

还有一点则是关于拦截器的优先级的问题,可以看到关于拦截器的两个变量interceptorsIndex是一个TreeMap,在put的会校验是否有相同的key,则相当于,不能定义有相同优先级的拦截器,然后拦截器被put进这个TreeMap的时候,会根据起优先级来有序放入。

在InterceptorServiceImpl,interceptorsIndex里面存放的拦截器会被挨着取出来,放到interceptors这个ArrayList里面,然后在执行拦截操作的时候,从interceptors依次出去拦截器,来对postcard执行拦截操作。

class Warehouse {
    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

}
public class UniqueKeyTreeMap<K, V> extends TreeMap<K, V> {
    private String tipText;

    public UniqueKeyTreeMap(String exceptionText) {
        super();
        tipText = exceptionText;
    }

    @Override
    public V put(K key, V value) {
        if (containsKey(key)) {
            throw new RuntimeException(String.format(tipText, key));
        } else {
            return super.put(key, value);
        }
    }
}

参考文章

developer.aliyun.com/article/716…

juejin.cn/post/684490…

juejin.cn/post/694561…

juejin.cn/post/684490…

juejin.cn/post/684490…

juejin.cn/post/684490…

juejin.cn/post/698126…

www.jianshu.com/p/f524abda3…

blog.csdn.net/Lebron_xia/…

www.jianshu.com/p/b8353fa94…

www.jianshu.com/p/746b9bed4…