阿里ARouter使用及源码解析(一)

在app的开发中,页面之间的相互跳转是最基本常用的功能。在Android中的跳转一般通过显式intent和隐式intent两种方式实现的,而Android的原生跳转方式会存在一些缺点:

  • 显式intent的实现方式,因为会存在直接的类依赖的问题,导致耦合严重;
  • 隐式intent的实现方式,则会出现规则集中式管理,导致协作变得困难;
  • 可配置性较差,一般而言配置规则都是在Manifest中的,这就导致了扩展性较差;
  • 跳转过程无法控制,一旦使用了StartActivity()就无法插手其中任何环节了,只能交给系统管理;
  • 当多组件化开发,使用原生的路由方式很难实现完全解耦;

而阿里的ARouter路由框架具有解耦、简单易用、支持多模块项目、定制性较强、支持拦截逻辑等诸多优点,很好的解决了上述的问题。关于ARouter具体实现功能,典型应用以及相应技术方案实现的介绍不在这详细介绍,具体可参见开源最佳实践:Android平台页面路由框架ARouter

阿里ARouter的分析计划

基本功能使用

1.添加依赖和配置

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

dependencies {
    compile 'com.alibaba:arouter-api:1.2.1.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.2.1'
    ...
}

2.添加注解

// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/test1")
public class Test1Activity extends AppCompatActivity{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test1);
    }
}

3.初始化SDK

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn1,btn2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);

        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn1) {
            // 如果使用了InstantRun,必须在初始化之前开启调试模式,但是上线前需要关闭,InstantRun仅用于开发阶段,
            // 线上开启调试模式有安全风险,可以使用BuildConfig.DEBUG来区分环境
            ARouter.openDebug();
            ARouter.init(getApplication()); // 尽可能早,推荐在Application中初始化
        }
    }
}

4.发起跳转操作

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn1,btn2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);

        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn1) {
            ....
        } else if (v.getId() == R.id.btn2){
            ARouter.getInstance().build("/test/test1").navigation();
        }
    }
}

以上相关代码就是ARouter的最基本功能使用的步骤,下面来分析跳转功能是如何实现的。

原理分析
1.ARouter编译的过程

ARouter在编译期的时候,利用自定义注解完成了页面的自动注册。相关注解源码参见arouter-annotation,编译处理器源码参见arouter-compiler

下面是注解@Route的源码介绍:

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

    /**
     *路由的路径,标识一个路由节点
     */
    String path();

    /**
     * 将路由节点进行分组,可以实现按组动态加载
     */
    String group() default "";

    /**
     * 路由节点名称,可用于生成javadoc文档
     */
    String name() default "undefined";

    /**
     * 用32位int类型标示,可用于页面的一些配置
     */
    int extras() default Integer.MIN_VALUE;

    /**
     * 路由的优先级
     */
    int priority() default -1;
}

Route中的extra值是个int值,由32位表示,即转换成二进制后,一个int中可以配置31个1或者0,而每一个0或者1都可以表示一项配置(排除符号位),如果从这31个位置中随便挑选出一个表示是否需要登录就可以了,只要将标志位置为1,就可以在声明的拦截器中获取到这个标志位,通过位运算的方式判断目标页面是否需要登录。所以可以通过extra给页面配置30多个属性,然后在拦截器中去进行处理。
ARouter在拦截器中会把目标页面的信息封装一个类Postcard,这个类就包含了目标页面注解上@Route标识的各种信息。关于拦截器的使用以及源码分析,后续会有介绍。

将代码编译一遍,可以看到ARouter生成下面几个源文件:

上面三个文件均是通过注解处理器RouteProcessor生成的,关于如何自定义注解处理器,可以阅读Android编译时注解APT实战(AbstractProcessor),同时也需要学习JavaPoet的基本使用。下面我们看RouteProcessor是如何生成相关文件的。

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //判断被注解了的元素集合是否为空
        if (CollectionUtils.isNotEmpty(annotations)) {
            //获取所有被@Route注解的元素集合,Element可以是类、方法、变量等
            Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
            try {
                logger.info(">>> Found routes, start... <<<");
                //具体处理注解,生成java文件的方法
                this.parseRoutes(routeElements);

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

        return false;
    }

process()方法相当于处理器的主函数main(),可以在这个方法中扫描、评估和处理注解的代码,以及生成Java文件。RouteProcessor中调用了parseRoutes(),用来处理所有被@Route注解的元素。在分析上述三个java文件如何生成之前,先看看生成文件的具体代码。

  • ARouter$$Root$$app类
public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("test", ARouter$$Group$$test.class);
  }
}
  • ARouter$$Group$$test类
public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/test/test1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/test1", "test", null, -1, -2147483648));
  }
}
  • ARouter$$Providers$$app类
public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
  }
}

我们接着分析上述三个文件是如何生成的

1.首先获取生成方法的参数的类型和参数名称

 private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
        if (CollectionUtils.isNotEmpty(routeElements)) {
          
            logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");

            rootMap.clear();
             // TypeElement 表示一个类或接口元素
            // public static final String ACTIVITY = "android.app.Activity";
            //得到类activity元素
            TypeElement type_Activity = elementUtil.getTypeElement(ACTIVITY);
            // public static final String SERVICE = "android.app.Service";
            //得到类service的元素
            TypeElement type_Service = elementUtil.getTypeElement(SERVICE);
            // public static final String SERVICE = "android.app.Fragment";
            TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType();
             // public static final String SERVICE = "android.support.v4.app.Fragment";
            TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();

            // public static final String IROUTE_GROUP = "com.alibaba.android.arouter.facade.template.IRouteGroup";
            //得到接口IRouteGroup元素
            TypeElement type_IRouteGroup = elementUtil.getTypeElement(IROUTE_GROUP);
          // public static final String IROUTE_GROUP = "com.alibaba.android.arouter.facade.template.IProviderGroup";
            //得到接口IProviderGroup元素
            TypeElement type_IProviderGroup = elementUtil.getTypeElement(IPROVIDER_GROUP);
            //获取RouteMeta,RouteType类名
            ClassName routeMetaCn = ClassName.get(RouteMeta.class);
            ClassName routeTypeCn = ClassName.get(RouteType.class);

            //下面代码是获取生成java文件中方法的参数类型名称和参数名称。
            /*
              获取获取ARouter$$Root$$app 类中方法参数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))
                    )
            );

            /*
              获取ARouter$$Group$$test,ARouter$$Providers$$app类中方法参数 Map<String, RouteMeta>类型的名称
             */
            ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
                    ClassName.get(Map.class),
                    ClassName.get(String.class),
                    ClassName.get(RouteMeta.class)
            );

            /*
             获取相关的参数
             */
            //获取ARouter$$Root$$app 类中方法的参数Map<String, Class<? extends IRouteGroup>> routes
            ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
           //获取ARouter$$Group$$test类中方法的参数Map<String, RouteMeta> atlas
            ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
             //获取ARouter$$Providers$$app类中方法的参数Map<String, RouteMeta> providers
            ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build();  

          
          .....
        }
    }

2.获取了方法的参数的类型和参数名称后,下面便是生成相应的方法

 private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
        if (CollectionUtils.isNotEmpty(routeElements)) {
            ........

            /*
              首先创建ARouter$$Root$$xxx 类中的loadInto()方法
              @Override
              public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {}
             */
            MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(rootParamSpec);

            //  遍历所有被@Route注解的元素
            for (Element element : routeElements) {
                TypeMirror tm = element.asType();
                Route route = element.getAnnotation(Route.class);
                RouteMeta routeMete = null;
                
                //判断该元素否为 Activity 、IProvider 、 Service 的子类,然后创建相应的RouteMeta 对象
                if (typeUtil.isSubtype(tm, type_Activity.asType())) {                 // Activity
                    logger.info(">>> Found activity route: " + tm.toString() + " <<<");

                    // 如果是acitiviy类型,获取所有被@Autowired的属性
                    //关于@Autowired的注解,我们之后再进行分析
                    Map<String, Integer> paramsType = new HashMap<>();
                    for (Element field : element.getEnclosedElements()) {
                        if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !typeUtil.isSubtype(field.asType(), iProvider)) {
                            // It must be field, then it has annotation, but it not be provider.
                            Autowired paramConfig = field.getAnnotation(Autowired.class);
                            paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), TypeUtils.typeExchange(field.asType()));
                        }
                    }
                    // ACTIVITY类型节点
                    routeMete = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
                } else if (typeUtil.isSubtype(tm, iProvider)) {         // IProvider
                    logger.info(">>> Found provider route: " + tm.toString() + " <<<");
                    //从该判断可看出,如果要想成功注册一个 PROVIDER 类型的路由节点,
                    //一定要实现 com.alibaba.android.arouter.facade.template.IProvider 这个接口
                    routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);
                } else if (typeUtil.isSubtype(tm, type_Service.asType())) {           // Service
                    logger.info(">>> Found service route: " + tm.toString() + " <<<");
                     //SERVICE类型节点
                    routeMete = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
                } else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
                    logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
                   //FRAGMENT类型节点
                    routeMete = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
                }
                
                //routeMete包含了每个路由节点的各种信息,下面的方法的主要功能就是根据@Route注解信息对节点进行分组,保存在groupMap集合中。
               //关于方法的具体实现,后面会有解析
                categories(routeMete);
          
            }

            .........
        }
    }

以上代码主要功能就是遍历所有被@Route注解的元素,然后将每个路由节点的信息按照类型(ACTIVITY类型,实现了IProvider 接口类型以及SERVICE类型)封装到RouteMeta中,最后调用categories(routeMete)方法将节点分组,保存在groupMap集合。

继续往下分析

 private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
        if (CollectionUtils.isNotEmpty(routeElements)) {
            ........

             /*
              然后创建ARouter$$Providers$$xxx 类中的loadInto()方法
             @Override
             public void loadInto(Map<String, RouteMeta> providers) {}
             */
            MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(providerParamSpec);

            //遍历分组的集合,生成相应的java文件
           //因为本文使用的例子没有对页面进行分组,所以只生成了一个组文件ARouter$$Group$$xxx
            for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
                String groupName = entry.getKey();
               /*
                  创建ARouter$$Group$$xxx 类中的loadInto()方法
                 @Override
                 public void loadInto(Map<String, RouteMeta> atlas) {}
             */
                MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                        .addAnnotation(Override.class)
                        .addModifiers(PUBLIC)
                        .addParameter(groupParamSpec);

                // 生成loadInto()方法体
                Set<RouteMeta> groupData = entry.getValue();
                //遍历每个组里面的路由节点
                for (RouteMeta routeMeta : groupData) {
                    switch (routeMeta.getType()) {
                        //如果节点类型是PROVIDER,
                        case PROVIDER:  
                          //获取路由节点元素的接口集合
                            List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
                            for (TypeMirror tm : interfaces) {
                             if (types.isSameType(tm, iProvider)) {   // Its implements iProvider interface himself.
                                   //路由节点元素其中一个接口是 com.alibaba.android.arouter.facade.template.IProvider 
                                  //给ARouter$$Providers$$xxx 类中的loadInto()添加方法体
                                    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.get((TypeElement) routeMeta.getRawType()),
                                            routeMeta.getPath(),
                                            routeMeta.getGroup());
                                } else if (types.isSubtype(tm, iProvider)) {
                                   //路由节点元素其中一个接口是com.alibaba.android.arouter.facade.template.IProvider 接口的子类型
                                    loadIntoMethodOfProviderBuilder.addStatement(
                                            "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                                            tm.toString(),   //IProvider子类型的全名
                                            routeMetaCn,
                                            routeTypeCn,
                                            ClassName.get((TypeElement) routeMeta.getRawType()),
                                            routeMeta.getPath(),
                                            routeMeta.getGroup());
                                }
                            //上面方法体的代码为:
                          //providers.put("实现接口的名称", RouteMeta.build(RouteType.PROVIDER, 类名.class,   "@Route.path", "@Route.group", null, @Route.priority, @Route.extras));
                            }
                            break;
                        default:
                            break;
                    }

                    // 将路由节点中被@Autowired注解的属性集合转换成字符串
                    StringBuilder mapBodyBuilder = new StringBuilder();
                    //获取路由节点中被@Autowired注解的属性集合
                    Map<String, Integer> paramsType = routeMeta.getParamsType();
                    if (MapUtils.isNotEmpty(paramsType)) {
                        for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
                            mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");
                        }
                    }
                    String mapBody = mapBodyBuilder.toString();
                    
                    //给ARouter$$Group$$xxx 类中的loadInto()添加方法体
                    //注意:有多个分组就会创建多个组文件
                    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.get((TypeElement) routeMeta.getRawType()),
                            routeMeta.getPath().toLowerCase(),
                            routeMeta.getGroup().toLowerCase());
                }

                  // 真正生成ARouter$$Group$$test JAVA文件
                 //NAME_OF_GROUP = ARouter$$Group$$
                //  groupName = test; 关于groupname的值在方法categories(routeMete)中会有讲解
                String groupFileName = NAME_OF_GROUP + groupName;
                JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                        TypeSpec.classBuilder(groupFileName)
                                .addJavadoc(WARNING_TIPS)
                                .addSuperinterface(ClassName.get(type_IRouteGroup))
                                .addModifiers(PUBLIC)
                                .addMethod(loadIntoMethodOfGroupBuilder.build())
                                .build()
                ).build().writeTo(mFiler);

                logger.info(">>> Generated group: " + groupName + "<<<");
                //将生成的组文件放在rootmap集合中去,为下面生成ARouter$$Root$$xxx文件做准备
                rootMap.put(groupName, groupFileName);
            }

         .......
        }
    }

以上代码主要功能由几点:

  • 遍历groupmap集合给ARouter$$Group$$xxx类中的loadInto()添加方法体,并且生成ARouter$$Group$$xxx JAVA文件,而文件命名为ARouter$$Group$$+groupname,其中有多个分组就会创建多个组文件。比如AROUTER源码中的样例就生成了多个分组文件
两个分组文件

关于生成的loadInto()中的方法体的例子,来自 AROUTER源码中的样例:

public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    //存在被@Autowired注解参数生成的代码
    atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("name", 18); put("boy", 0); put("age", 3); put("url", 18); }}, -1, -2147483648));
    .....
   //没有被@Autowired注解参数生成的代码
    atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
    ....
  }
}
  • 遍历每个组里面的路由节点,查找节点类型是否为PROVIDER类型,如果是就向给ARouter$$Providers$$xxx类中的loadInto()添加方法,其文件命名ARouter$$Providers$$+modulename。关于生成的loadInto()中的方法体的例子,来自 AROUTER源码中的样例:
public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
    //路由节点元素其中一个接口是IProvider的子类型
    providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
     //路由节点元素其中一个接口是IProvider接口
    providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
  }
}
  • 将生成的组文件放在rootmap集合中去,为下面生成ARouter$$Root$$xxx文件做准备,其文件命名ARouter$$Root$$+modulename。

我们接着分析parseRoutes()方法最后一段代码,这段代码其实很简单,主要目的就是给ARouter$$Root$$xxx的loadInto()添加方法体,最后生成Router$$Providers$$xxx,ARouter$$Root$$xxx文件

 private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
        if (CollectionUtils.isNotEmpty(routeElements)) {
            ........
            //遍历rootMap集合,给ARouter$$Root$$xxx的`loadInto()`添加方法体
            if (MapUtils.isNotEmpty(rootMap)) {
                // Generate root meta by group name, it must be generated before root, then I can findout 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()));
                }
            }

            // 生成Router$$Providers$$xxx文件
            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$$xxx文件
            String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                    TypeSpec.classBuilder(rootFileName)
                            .addJavadoc(WARNING_TIPS)
                            .addSuperinterface(ClassName.get(elementUtil.getTypeElement(ITROUTE_ROOT)))
                            .addModifiers(PUBLIC)
                            .addMethod(loadIntoMethodOfRootBuilder.build())
                            .build()
            ).build().writeTo(mFiler);

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

关于生成的loadInto()中的方法体的例子,来自 AROUTER源码中的样例:

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

上面分析的便是parseRoutes()方法所有代码的解析

3.最后我们看下categories()方法是如何分组的

   private void categories(RouteMeta routeMete) {
        //如果路由路径合法,且有groupname进行执行
        if (routeVerify(routeMete)) {
            logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
             //根据groupname获取该组的路由节点集合,如果集合为空,则创建一个新的组,将该节点添加进去,并将组集合保存在groupmap中;
          //不为空,则添加到所属的组集合中去
            Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
            if (CollectionUtils.isEmpty(routeMetas)) {
                Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {
                    @Override
                    public int compare(RouteMeta r1, RouteMeta r2) {
                        try {
                            return r1.getPath().compareTo(r2.getPath());
                        } catch (NullPointerException npe) {
                            logger.error(npe.getMessage());
                            return 0;
                        }
                    }
                });
                routeMetaSet.add(routeMete);
                groupMap.put(routeMete.getGroup(), routeMetaSet);
            } else {
                routeMetas.add(routeMete);
            }
        } else {
            logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
        }
    }

//判断路由路径是否合法,并且设置groupname
 private boolean routeVerify(RouteMeta meta) {
        String path = meta.getPath();
        //如果路径为空,或者不是由'/'开头,返回false
        if (StringUtils.isEmpty(path) || !path.startsWith("/")) {   // The path must be start with '/' and not empty!
            return false;
        }

         //如果在@Route注解中没有设置group标识,那么就默认取path路径第一段路径名作为groupname
        if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)
            try {
                String defaultGroup = path.substring(1, path.indexOf("/", 1));
                if (StringUtils.isEmpty(defaultGroup)) {
                    return false;
                }

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

        return true;
    }

通过分析,如果@Route注解中有设置group标识,作为groupname,如果没有就取/xxx1/xxx2,xxx1作为groupname,并将同一组的路由节点放到同一个集合中去。

至此关于@Route注解在编译期时生成ARouter$$Root$$xxx,Router$$Providers$$xxx,ARouter$$Group$$xxx三种映射文件的源码分析完毕。

2.ARouter初始化过程

ARouter经过代码编译后,生成了相应的映射文件,我们可以断定,ARouter 的初始化会将这些文件加载到内存中去,形成一个路由表,以供后面路由查找跳转之用。其相关源码可参见 arouter-api

  • ARouterinit()方法
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.");
        }
    }

由上面代码可以看出,其初始化实际上是调用了_ARouterinit ()方法,而且其他的跳转方法最终调用的也是_ARouter 种的方法。

  • _ARouterinit()方法
  protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        logger.info(Consts.TAG, "ARouter init success!");
        hasInit = true;

        return true;
    }

_ARouter中又调用了LogisticsCenter.init(),继续追踪下去,其中传入了一个线程池executor,这个线程池在拦截器的时候会使用到。

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

        try {
             //ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"
            // 获取ROUTE_ROOT_PAKCAGE 包里面的所有文件
            List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

            //遍历所有ROUTE_ROOT_PAKCAGE 包里的文件
            for (String className : classFileNames) {
                //文件名以“com.alibaba.android.arouter.routes.ARouter$$Root”开头执行下面代码
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // 通过反射实例化,并且调用loadInto(),目的即是将编译生成的ARouter$$Group$$xxx文件加载到内存中,保存在Warehouse.groupsIndex;
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    //文件名以“com.alibaba.android.arouter.routes.ARouter$$Interceptors”开头执行下面代码
                    //  执行编译生成的ARouter$$Interceptors$$xxx的loadInto(),将自定义拦截器类存放在Warehouse.interceptorsIndex中
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                     //文件名以“com.alibaba.android.arouter.routes.ARouter$$Providers”开头执行下面代码
                   //  执行编译生成的ARouter$$Interceptors$$xxx的loadInto()
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }

            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() + "]");
        }
    }
  • _ARouterafterInit()方法
static void afterInit() {
        // 通过路由机制,初始化路由拦截机制。关于路由拦截机制的使用和原理,后续文章会有分析
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }

以上就是ARouter初始化的所有代码,关于如何查找到com.alibaba.android.arouter.routes包内所有文件这里便不做过多分析,大家可以去阅读 arouter-apiClassUtils这个类的源码。
总结下来,其实ARouter 的初始化只做了一件事,找到自己编译期产生的清单文件,把 Group 、Interceptor 、Provider 三种清单加载到 Warehouse 内存仓库中。即下面这些文件,来源自AROUTER源码中的样例

值得注意的是,在初始化阶段,ARouter 仅载入了 Group 清单,并没有具体载入每个 Group 中包含的具体的路由节点清单,只有当使用到具体的 Group 时,才会加载对应的 Group 列表。这种分组管理,按需加载,大大的降低了初始化时的内存压力。并且Warehouse类中保存了路由清单,并且将使用过的路由对象缓存起来,之后查找都是直接使用缓存的对象 。

3.ARouter调用过程分析

页面跳转最基本方法

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

获取Provider服务(实现了IProvider接口以及IProvider子类接口的服务类)的方法有两种:

1.byName方式
ARouter.getInstance().build("/service/hello").navigation()

2.byType方式
ARouter.getInstance().navigation(HelloService.class)

ARouter路由跳转采用链式调用,ARouter.getInstance()其中采用的单例模式,获取ARouter的实例,这个就不作过多分析,主要分析build()navigation()

build()方法
ARouter的build(String path)init()方法一样,调用的是_ARouterbuild(String path)方法。

  protected Postcard build(String path) {
        if (TextUtils.isEmpty(path)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return build(path, extractGroup(path));
        }
    }

其中extractGroup(String path)就是根据path获取分组名,即path第一段“/”符号之间的值

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

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

build(String path)方法最终调用的是build(String path, String group)

    protected Postcard build(String path, String group) {
        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return new Postcard(path, group);
        }
    }

值得注意的是其中ARouter.getInstance().navigation(PathReplaceService.class)就是得到实现PathReplaceService接口的一个服务对象,对原始path进行处理后,生成新的path路径。而这个类需要我们自己自定义去实现,如果没有实现,pService=null,原始path不做任何处理。
下面是PathReplaceService接口,我们可以通过实现forString()forUri()方法,对某些url进行替换处理,跳转到其他的目标页面。

public interface PathReplaceService extends IProvider {

    /**
     * For normal path.
     *
     * @param path raw path
     */
    String forString(String path);

    /**
     * For uri type.
     *
     * @param uri raw uri
     */
    Uri forUri(Uri uri);
}

最后返回一个Postcard实例对象,里面封装了路由节点的路径,分组等节点信息。其实build()方法的目的只有一个就是根据路由,封装成Postcard对象,其对象贯穿之后整个路由过程。Postcard 包含了众多的属性值,提供了路由过程中所有的控制变量。

public final class Postcard extends RouteMeta {
    private Uri uri;
    private Object tag;             // A tag prepare for some thing wrong.
    private Bundle mBundle;         // 传递的参数
    private int flags = -1;         // intent 的flag标志
    private int timeout = 300;      // Navigation timeout, TimeUnit.Second !
    private IProvider provider;     // IProvider服务对象
    private boolean greenChannal;
    private SerializationService serializationService;//序列化服务对象

     // 跳转动画
    private Bundle optionsCompat;    // The transition animation of activity
    private int enterAnim;
    private int exitAnim;

    // copy from RouteMeta 
    private RouteType type;         // 路由节点类型
    private Element rawType;        
    private Class<?> destination;  //需要跳转到的页面
    private String path;            // 路径
    private String group;           // 分组
    private int priority = -1;      // 优先级
    private int extra;              // 配置标识
    private Map<String, Integer> paramsType;  // 路由页面被@Autowired注解属性
    // ......
}

navigation()方法
关于页面跳转的navigation()方法有多个重载的方法,但最终都会调用_ARouter下面这个方法

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            //首先对postcard进行一些处理,设置postcard的destination,type,priority 等一些属性值,completion()后面会有分析
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());

            if (debuggable()) { // Show friendly tips for user.
                Toast.makeText(mContext, "There's no route matched!\n" +
                        " Path = [" + postcard.getPath() + "]\n" +
                        " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
            }
            // 如果处理postcard失败,通过 callback 回调失败结果
           // callback为空的情况下,如果有定义全局的降级处理(DegradeService),则使用全局处理
           //降级处理也需要我们自己实现DegradeService接口
            if (null != callback) {
                callback.onLost(postcard);
            } else {    // No callback for this invoke, then we use the global degrade service.
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

            return null;
        }
         //路由处理成功,回调callback.onFound()
        if (null != callback) {
            callback.onFound(postcard);
        }
        
        //目前来说,PROVIDER服务类型,以及FRAGMENT类型不需要通过拦截器外,其他类型均需要通过拦截器
        //关于拦截器相关用法及原理分析在后续的文章中会讲解到,大家去可以关注下
        if (!postcard.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;
    }

值得注意的是,当跳转路由处理失败的时候,会获取一个降级服务,我们可以实现DegradeService接口,实现onLost()方法,对路由处理失败的情况进行处理,比如跳转到一个信息提示页面,让用户去更新版本等操作等。 下面是DegradeService接口:

public interface DegradeService extends IProvider {

    /**
     * Router has lost.
     *
     * @param postcard meta
     */
    void onLost(Context context, Postcard postcard);
}

通过上面代码的分析,不管是否通过拦截器进行处理,最后都会调用_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:
                //下面就是最基本的使用intent进行activity进行跳转
                // 创建intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                //设置传参
                intent.putExtras(postcard.getExtras());

                //activity启动标志
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // 在主线程中进行跳转
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        //新版本带转场动画的启动方式
                        if (requestCode > 0) {  // Need start for result
                            ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
                        } else {
                            ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
                        }

                        if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
                            //老版本的跳转动画
                            ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
                        }
                        //跳转成功,回调callback.onArrival()
                        if (null != callback) { // Navigation over.
                            callback.onArrival(postcard);
                        }
                    }
                });

                break;
            case PROVIDER:
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                Class fragmentMeta = postcard.getDestination();
                try {
                     //实例化fragment,并传递参数
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }

                    return instance;
                } catch (Exception ex) {
                    logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

目前仅ARouter实现了 ACTIVITY , PROVIDER ,FRAGMENT三种种类型。上面关于postcard的provider,destination的值都是在completion()中设置的。我们接着看LogisticsCentercompletion(Postcard postcard)

    public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }
      
        // 查找Warehouse仓库的路由节点缓存,看是否已在缓存中
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {   
          // 如果没有,查找仓库的组别清单中是否存在该组别,组别清单已经在初始化的时候加载到仓库中去了
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  
            //如果没有抛出异常
            if (null == groupMeta) {
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                // Load route and cache it into memory, then delete from metas.
                try {
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                    // 实例化个组别的类,调用loadInto(),将组别中所有的路由节点加载进仓库Warehouse.routes,缓存
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                     // 从组别清单中删除已加载的组别,防止重复加载
                    Warehouse.groupsIndex.remove(postcard.getGroup());

                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }
                //当路由节点加载到缓存中去后,重新查找执行else代码,对postcard进行处理
                completion(postcard);   // Reload
            }
        } else {
            //给postcard设置destination,type,priority等值,供上面讲解到的_navigation()进行使用
            // 其中routeMeta是在ARouter$$Group$$xxx的loadInto中创建的
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            //如果通过build(Uri url) 进行跳转的话 通过解析url ,将传参保存进bundle中
            Uri rawUri = postcard.getUri();
            if (null != rawUri) {  
                //splitQueryParameters()就是在uri中携带的参数进行解析
                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 autoinject.
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }

                // Save raw uri
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }
            
            //从这里也可以看出PROVIDER,FRAGMENT不需要通过拦截器
            switch (routeMeta.getType()) {
                case PROVIDER:  
                    // 如果是PROVIDER节点类型,从服务节点列表中获取,如果没有,则实例化,并保存在服务节点列表Warehouse.providers中
                  //并将实例化的对象设置给postcard的provider属性
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) { // There's no instance of this provider
                        IProvider provider;
                        try {
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

分析到这里,关于页面基本跳转的原理分析就已经结束了。最后就是关于获取Provider服务两种方法的源码分析。其中byName方式,和页面跳转是一模一样的。我们只需要看看byType方式即可。byType方式最后调用的是_ARouternavigation(Class<? extends T> service)

  protected <T> T navigation(Class<? extends T> service) {
        try {
            // 通过 className 获取 Postcard 对象
            Postcard postcard = LogisticsCenter.buildProvider(service.getName());

            // 兼容1.0.5 compiler sdk版本.
            if (null == postcard) { // No service, or this service in old version.
                postcard = LogisticsCenter.buildProvider(service.getSimpleName());
            }
           // 对 Postcard 对象进行处理
            LogisticsCenter.completion(postcard);
             //返回 Postcard 中的 provider 属性值
            return (T) postcard.getProvider();
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            return null;
        }
    }

上面代码中的completion()方法之前已经分析过了,只需要看下LogisticsCenter.buildProvider(service.getName())即可。

  public static Postcard buildProvider(String serviceName) {
        RouteMeta meta = Warehouse.providersIndex.get(serviceName);

        if (null == meta) {
            return null;
        } else {
            return new Postcard(meta.getPath(), meta.getGroup());
        }
    }

这个方法非常的简单,就是根据服务类名去仓库Warehouse.providersIndex中获去路由节点元素,然后封装在Postcard对象中。服务类清单列表Warehouse.providersIndex中的值是在初始化时缓存的。值得注意的是,PROVIDER 类型的路由节点既存在于对应的分组中,也存在于服务类清单列表中。所以,ARouter 可通过byType,byName两种方式来获取

补充

关于ARouter的基本用法上面只有最基本跳转的介绍,下面对其他一些基本使用进行下补充

  • 带参数跳转
//1.传递参数
 ARouter.getInstance().build("/test/activity1")
                        .withString("name", "老王")
                        .withInt("age", 18)
                        .withBoolean("boy", true)
                        .withLong("high", 180)
                        .withString("url", "https://a.b.c")
                        .withParcelable("pac", testParcelable)
                        .withObject("obj", testObj)
                        .navigation();

//2.直接传递Bundle
  Bundle params = new Bundle();
  ARouter.getInstance()
          .build("/test/activity1")
          .with(params)
          .navigation();

这些传参都是保存在生成的postcard对象中的mBundle属性里,然后在跳转的时候通过intent.putExtras(postcard.getExtras())达到传送参数的目的。
值得注意的是,关于对象的传递有两种,一种是withParcelable()方法,不过此方法需要传递的对象实现Parcelable接口,达到序列化的目的;另外一种是withObject()方法,此方法的原理是将实体类转换成json字符串,通过String的方式进行传递,而且使用这种方式需要实现 SerializationService,并使用@Route注解标注,下面是ARouter样例:

@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
    @Override
    public void init(Context context) {

    }

    @Override
    public <T> T json2Object(String text, Class<T> clazz) {
        return JSON.parseObject(text, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance);
    }
}

而且,需要在跳转到的页面获取JsonServiceImpl服务,将json字符串转换成对象。

SerializationService serializationService = ARouter.getInstance().navigation(SerializationService.class);
TestObj obj = serializationService.json2Object(getIntent().getString("obj"), TestObj.class);
  • 带返回结果跳转
ARouter.getInstance().build("/test/activity2").navigation(this, 666);

值得注意的是,这时候的 navigation需要传递activit和requestCode。

  • 获取Fragment的实例

定义一个fragment

@Route(path = "/test/fragment")
public class BlankFragment extends Fragment {
    public BlankFragment() {
        //必须要一个空的构造器
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        TextView textView = new TextView(getActivity());
        return textView;
    }

}

获取frament

Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
  • 带转场动画跳转
// 转场动画(常规方式)
 ARouter.getInstance() .build("/test/activity2")
                      .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
                      .navigation(this);

// 转场动画(API16+)
 ActivityOptionsCompat compat = ActivityOptionsCompat.makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);
ARouter.getInstance().build("/test/activity2").withOptionsCompat(compat) .navigation();
  • 获取服务

服务是全局单例的,只有在第一次使用到的时候才会被初始化。
暴露服务,必须实现IProvider 接口 或者其子类型

// 声明接口,其他组件通过接口来调用服务
public interface HelloService extends IProvider {
    String sayHello(String name);
}

// 实现接口
@Route(path = "/service/hello", name = "测试服务")
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
    return "hello, " + name;
    }

    @Override
    public void init(Context context) {

    }
}

获取服务

//bytype
HelloService helloService1 = ARouter.getInstance().navigation(HelloService.class);
//byname
HelloService helloService2 = (HelloService) ARouter.getInstance().build("/service/hello").navigation();
  • 多模块结构

app中可能存在多个模块,每个模块下面都有一个root结点,每个root结点都会管理整个模块中的group节点,每个group结点则包含了该分组下的所有页面,而每个模块允许存在多个分组,每个模块中都会有一个拦截器节点就是Interceptor结点,除此之外每个模块还会有控制拦截反转的provider结点

最后

到此,关于ARouter的基本用法以及原理分析的就全部结束了,如果有不清楚或者错误的地方,希望各位同学指出。关于ARouter拦截器,各种服务,依赖注入等更多进阶用法及源码分析会更新在后续的文章。

如果各位同学认为本文对你有一些帮助,希望能点个喜欢,谢谢!

推荐阅读更多精彩内容