阿里路由框架ARouter源码解析之Compiler

前段时间,公司项目在做组件化重构,过程中当然会有很多痛点。

成都创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:做网站、网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的稷山网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

组件化最重要的是根据项目和业务进行分模块,至于模块的粒度就看大家自己来把控了!

这里要说的就是模块之间的数据传输问题

组件化之后,各个模块不相互依赖,那么怎么相互跳转和传递数据呢?

答案就是通过隐式Intent 的方式来跳转和传递数据。

以往的显示Intent 跳转,会存在类直接依赖的问题,这样会导致耦合性非常严重;相比而言,隐式Intent则不需要类之间的直接依赖,但是会出现规则集中式管理,扩展性比较差。

所以在调研期间就发现阿里开源了ARouter–路由框架。

ARouter的好处我这里就不多说,大家可以去看官方文档或者去github上看README。

【https://github.com/alibaba/ARouter】

接下来会分为若干篇blog来分析一下ARouter的源码!

看了ARouter的源码就会发现,它提供了两个SDK,一个是API,一个Compiler。

  • Compiler SDK 是用于编译器生成相关类文件的。
  • API SDK 是用在运行期间路由跳转等作用的。

阿里路由框架ARouter 源码解析之Compiler

这里先说说Compiler层SDK。

RouteProcessor 路由路径处理器

InterceptorProcessor 拦截器处理器

AutowireProcessor 自动装配处理器

注解处理器的处理流程

阿里路由框架ARouter 源码解析之Compiler

(图片转自网络)

实际上,Compiler SDK 只是处根据扫描到的注解生成相应的映射(java)文件。

最后一步通过固定包名加载映射文件是由API SDK来做的。

以官方demo为例来说:

阿里路由框架ARouter 源码解析之Compiler

上图所示就是ARouter在编译期间生成的类文件。

  • 红色标注的是 RouteProcessor 生成的类文件
  • 蓝色标注的是 InterceptorProcessor 生成的类文件
  • 橙色标书的是 AutowiredProcessor 生成的类文件

arouter-compiler的目录结构如下:

阿里路由框架ARouter 源码解析之Compiler

  • processor包下面是注解处理器
  • utils包下面是相关工具类

下面分别说说这三种注解处理器:

用过编译时注解的朋友们都知道,注解处理器需要继承AbstractProcessor ,主要涉及的函数有 init(),process() 这两个。

RouteProcessor

类的继承信息:

@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends AbstractProcessor {

init

init()

 // 初始化处理器
 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
  super.init(processingEnv);

  // 文件管理器
  mFiler = processingEnv.getFiler();     // Generate class.
  // 获取类型处理工具类
  types = processingEnv.getTypeUtils();   // Get type utils.
  // 获取日志信息工具类
  elements = processingEnv.getElementUtils();  // Get class meta.

  typeUtils = new TypeUtils(types, elements);
  // 封装日志信息类
  logger = new Logger(processingEnv.getMessager()); // Package the log utils.

  // 获取用户配置的[moduleName]
  Map options = processingEnv.getOptions();
  if (MapUtils.isNotEmpty(options)) {
   moduleName = options.get(KEY_MODULE_NAME);
  }

  if (StringUtils.isNotEmpty(moduleName)) {
   // 格式化
   moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");

   logger.info("The user has configuration the module name, it was [" + moduleName + "]");
  } else {
   // 如果没有在build.gradle中配置moduleName,则会抛出异常。
   logger.error("These no module name, at 'build.gradle', like :\n" +
     "apt {\n" +
     " arguments {\n" +
     "  moduleName project.getName();\n" +
     " }\n" +
     "}\n");
   throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
  }

  // 
  iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
  // RouterProcessor 初始化完毕
  logger.info(">>> RouteProcessor init. <<<");
 }
// Consts.java
public static final String KEY_MODULE_NAME = "moduleName";

在使用ARouter注解的时候,按照官方文档是需要在每个module里面的build.gradle中配置如下信息:

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

配置这个属性的目的,就是为了在编译期间生成相关module下的文件和存储文件名称。

process()

一般在process()函数中做的操作如下:

  1. 遍历注解的元素
  2. 检验元素是否符合要求(过滤元素)
  3. 获取输出类参数
  4. 生成映射文件(java文件)
  5. 错误处理
 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  if (CollectionUtils.isNotEmpty(annotations)) {
   // 获取所有添加Route注解的元素
   Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
   try {
    logger.info(">>> Found routes, start... <<<");
    // 调用arseRoute()函数进行处理获取的注解元素集合
    this.parseRoutes(routeElements);

   } catch (Exception e) {
    logger.error(e);
   }
   // 如果有Route元素的注解,并且处理过程中无异常则返回true
   return true;
  }
  // 否则返回false
  return false;
 }

parseRoutes()

这个函数的代码有点长,大家耐心看!

 // Consts.java

 public static final String ACTIVITY = "android.app.Activity";
 public static final String FRAGMENT = "android.app.Fragment";
 public static final String FRAGMENT_V4 = "android.support.v4.app.Fragment";
 public static final String SERVICE = "android.app.Service";

 private static final String FACADE_PACKAGE = "com.alibaba.android.arouter.facade";
 private static final String TEMPLATE_PACKAGE = ".template";

 public static final String IROUTE_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IRouteGroup";

 public static final String IPROVIDER_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IProviderGroup";
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
  if (CollectionUtils.isNotEmpty(routeElements)) {
   // ...

   rootMap.clear();

   // 获取ACTIVITY, SERVICE, FRAGMENT, FRAGMENT_V4 这四种 类型镜像
   TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType();
   TypeMirror type_Service = elements.getTypeElement(SERVICE).asType();
   TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType();
   TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();

   // ARouter的接口
   TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP);
   TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP);

   // 
   // 下面就是遍历获取的注解信息,通过javapoet来生成类文件了 

   ClassName routeMetaCn = ClassName.get(RouteMeta.class);
   ClassName routeTypeCn = ClassName.get(RouteType.class);

   /*
    ParameterizedTypeName用来创建类型对象,例如下面

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

   /*
    RouteMeta封装了路由相关的信息

    ```Map```
    */
   ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
     ClassName.get(Map.class),
     ClassName.get(String.class),
     ClassName.get(RouteMeta.class)
   );

   /*
    创建输入参数
    */

   // 1。 生成的参数:Map> routes
   ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build(); // 第一个参数表示参数类型,第二个函数表示参数名称

   // 2。 Map atlas
   ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();

   // 3。 Map providers
   ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); 

   // MethodSpec用来创建方法

   // public static final String METHOD_LOAD_INTO = "loadInto";
   /*
    Build method : 'loadInto'
    */
   MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
     .addAnnotation(Override.class) // override
     .addModifiers(PUBLIC) // public
     .addParameter(rootParamSpec); // 参数

   // 创建出来的函数如下
   /**
    * @Override
    * public void loadInto(Map> routes) { } 
    */ 

   //

   // 接下来的代码就是遍历注解元素,进行分组,进而声称java文件

   for (Element element : routeElements) { // 遍历每个元素

    TypeMirror tm = element.asType();
    Route route = element.getAnnotation(Route.class);
    RouteMeta routeMete = null;

    // 判断类型
    if (types.isSubtype(tm, type_Activity)) { // Activity
     logger.info(">>> Found activity route: " + tm.toString() + " <<<");

     Map paramsType = new HashMap<>();

     // 遍历查找所有添加 @AutoWired 注解的变量
     for (Element field : element.getEnclosedElements()) {

      // 1. 必须是field
      // 2. 必须有注解AutoWired
      // 3. 必须不是IProvider类型

      if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {
       // 满足上述条件后,获取注解
       Autowired paramConfig = field.getAnnotation(Autowired.class);

       // 看过源码就知道,Autowired支持写别名,当指定name属性之后,就会以name为准,否则以field的名字为准。
       // TypeUtils是自定义工具类,用来判断field的数据类型的,转换成int值。

       paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), typeUtils.typeExchange(field));
      }

     // 构建一条路由信息,将字段注解信息保存进去

     routeMete = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
    } 

    // 如果是IProvider类型的注解,则直接创建一条PROVIDER类型的路由信息
    else if (types.isSubtype(tm, iProvider)) {   
     routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);
    } 
    // 如果是Service类型的注解,则直接创建一条Service类型的路由信息
    else if (types.isSubtype(tm, type_Service)) {   // Service
     routeMete = new RouteMeta(route, element, RouteType.parse(Service), null);
    } 
    // 如果是fragmentTmV4类型的注解,则直接创建一条Fragment类型的路由信息
    else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
     routeMete = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
    }

    // 将路由信息进行分组 (每个路由信息对象中都保存着它所属的组别信息,在调用categories()函数之前所有的组别信息都是默认值"" )
    categories(routeMete);

   }

   // 第一次遍历之前,已经创建了ROOT类的loadInto函数
   // 下面开始创建Provider类的loadInto函数
   MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
     .addAnnotation(Override.class)
     .addModifiers(PUBLIC)
     .addParameter(providerParamSpec);

   // 创建出来的函数如下
   /**
    * @Override
    * public void loadInto(Map providers) { } 
    */   

   // 接着,遍历所有在 categories(routeMete); 得到的所有组别

   for (Map.Entry> entry : groupMap.entrySet()) {
    String groupName = entry.getKey();

    // 创建分组类的函数 -- loadInto(Map atlas)
    MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
      .addAnnotation(Override.class)
      .addModifiers(PUBLIC)
      .addParameter(groupParamSpec);

    // 往组别函数loadInto中添加数据
    Set groupData = entry.getValue();

    // PROVIDERL 类型的数据需要特殊处理

    for (RouteMeta routeMeta : groupData) {
     switch (routeMeta.getType()) {
      case PROVIDER: 

       List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
       // 遍历当前类的接口
       for (TypeMirror tm : interfaces) {
        // 如果当前类直接实现了IProvider接口
        if (types.isSameType(tm, iProvider)) { 

   // 这种情况下,在loadInfo()函数里面添加的语句类似于:
   // singleService直接实现IProvider接口

   /**
    * @Route(path = "/service/single")
    * public class SingleService implements IProvider
    * 
    * providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648)); 
    */         

         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)) {
         // 如果是接口继承的IProvider

   // 这种情况下,在loadInfo()函数里面添加的语句类似于:
   // singleService直接实现IProvider接口

   /**
    * @Route(path = "/service/hello")
    * public class HelloServiceImpl implements HelloService 
    * public interface HelloService extends IProvider
    * //
    * providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648)); 
    */            

         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.get((TypeElement) routeMeta.getRawType()),
           routeMeta.getPath(),
           routeMeta.getGroup());
        }
       }
       break;
      default:
       break;
     }

     // 拼接添加注解的字段

     StringBuilder mapBodyBuilder = new StringBuilder();
     Map paramsType = routeMeta.getParamsType();
     if (MapUtils.isNotEmpty(paramsType)) {
      for (Map.Entry types : paramsType.entrySet()) {
       mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");
      }
     }
     // // 形式如: put("pac", 9); put("obj", 10);
     String mapBody = mapBodyBuilder.toString();

     // 往loadInto函数里面添加一个语句
     loadIntoMethodOfGroupBuilder.addStatement(
       "atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
       routeMeta.getPath(), // 完整路径
       routeMetaCn, // RouteMeta
       routeTypeCn, // RouteType
       ClassName.get((TypeElement) routeMeta.getRawType()), // 注解原生类的名称
       routeMeta.getPath().toLowerCase(), // 完整路径
       routeMeta.getGroup().toLowerCase()); // 组名
    }

    // 添加的语句如下:
    // atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap(){{put("pac", 9); put("obj", 10); put("name", 8); put("boy", 0); put("age", 3); put("url", 8); }}, -1, -2147483648));

    // 生成组类别java文件
    // public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + "Group" + SEPARATOR;
    // public static final String SEPARATOR = "$$";
    // public static final String PROJECT = "ARouter";
    String groupFileName = NAME_OF_GROUP + groupName;

    JavaFile.builder(PACKAGE_OF_GENERATE_FILE, // package 名称 --"com.alibaba.android.arouter.routes"
      TypeSpec.classBuilder(groupFileName) //java类名
        .addJavadoc(WARNING_TIPS) // doc
        .addSuperinterface(ClassName.get(type_IRouteGroup)) // 添加继承的接口
        .addModifiers(PUBLIC) // 作用域为public
        .addMethod(loadIntoMethodOfGroupBuilder.build()) // 添加函数(包括了函数里面的代码块)
        .build()
    ).build().writeTo(mFiler);

    // 将组名和组文件名放到map中,方便按需加载
    rootMap.put(groupName, groupFileName);
   }

  // .................................................................... //


   // 经过了上面的for循环,生成了如 ARouter$$Group$$service.java 和ARouter$$Group$$test.java 文件,它们所在的包是 com.alibaba.android.arouter.routes。

   if (MapUtils.isNotEmpty(rootMap)) {
    // 遍历这些group,进而生成Root类文件
    for (Map.Entry entry : rootMap.entrySet()) {
     loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));

     // 每一个statement如: routes.put("test", ARouter$$Group$$test.class);
    }
   }

   // 生成provider类文件

   // provider文件名为:ARouter$$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);


   // 生成root文件
   // 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(elements.getTypeElement(ITROUTE_ROOT)))
       .addModifiers(PUBLIC)
       .addMethod(loadIntoMethodOfRootBuilder.build())
       .build()
   ).build().writeTo(mFiler);


  }
 }

categories()

下面来看一下怎么讲路由进行分组的

private void categories(RouteMeta routeMete) {
  // 首先去验证这条路由信息
  if (routeVerify(routeMete)) {
   // 尝试从groupMap中通过group名称获取路由信息
   Set routeMetas = groupMap.get(routeMete.getGroup());
   if (CollectionUtils.isEmpty(routeMetas)) { // 如果map中没有相关记录,则表示这个组别还未添加到map中
    Set routeMetaSet = new TreeSet<>(new Comparator() {
     @Override
     public int compare(RouteMeta r1, RouteMeta r2) {
      try {
       return r1.getPath().compareTo(r2.getPath());
      } catch (NullPointerException npe) {
       logger.error(npe.getMessage());
       return 0;
      }
     }
    });
    // 添加该组别到map中
    routeMetaSet.add(routeMete);
    groupMap.put(routeMete.getGroup(), routeMetaSet);
   } else { // 如果存在该组别则添加到这一组中
    routeMetas.add(routeMete);
   }
  } else {
   // 验证路由信息不正确是会在编译期间输出错误日志
   logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
  }
 }

routeVerify()

// 验证路由信息的正确性
private boolean routeVerify(RouteMeta meta) {
  String path = meta.getPath();
  // 判断路径是否为空或者是否以“/”开头
  if (StringUtils.isEmpty(path) || !path.startsWith("/")) { // The path must be start with '/' and not empty!
   return false;
  }
  // 没有分组时,group为""
  if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)
   try {
    // 截取字符串获取group
    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注解时,路径不允许为空且必须以“/”开头

RouteProcessor注解处理器生成的文件由三种:

1. ARouter$$Group$$xxx (可能有多个)
2. ARouter$$Providers$$xxx (只有一个)
3. ARouter$$Root$$xxx (只有一个)

InterceptorProcessor

@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)
public class InterceptorProcessor extends AbstractProcessor

init()

 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
  super.init(processingEnv);

  // ... 省略代码与RouteProcressor基本一样

  iInterceptor = elementUtil.getTypeElement(Consts.IINTERCEPTOR).asType();
 }

process()

 @Override
 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  if (CollectionUtils.isNotEmpty(annotations)) {
   // 获取Interceptor注解的集合
   Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
   try {
    // 处理注解信息
    parseInterceptors(elements);
   } catch (Exception e) {
    logger.error(e);
   }
   return true;
  }

  return false;
 }

parseInterceptors()

private Map interceptors = new TreeMap<>();
private void parseInterceptors(Set<? extends Element> elements) throws IOException {
  if (CollectionUtils.isNotEmpty(elements)) {
   // 遍历注解元素
   for (Element element : elements) {
    if (verify(element)) { // 做验证

     Interceptor interceptor = element.getAnnotation(Interceptor.class);
     // 尝试从拦截器结合中根据优先级获取
     Element lastInterceptor = interceptors.get(interceptor.priority());

     // 如果是已经存在相同优先级的拦截器,就会抛出异常
     if (null != lastInterceptor) { 
      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())
      );
     }
     // 添加到集合中
     interceptors.put(interceptor.priority(), element);
    } else {
     logger.error("A interceptor verify failed, its " + element.asType());
    }
   }

   // Interface of ARouter.
   TypeElement type_ITollgate = elementUtil.getTypeElement(IINTERCEPTOR);
   TypeElement type_ITollgateGroup = elementUtil.getTypeElement(IINTERCEPTOR_GROUP);

   /**
    * 创建类型对象
    *
    * ```Map>```
    */
   ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
     ClassName.get(Map.class),
     ClassName.get(Integer.class),
     ParameterizedTypeName.get(
       ClassName.get(Class.class),
       WildcardTypeName.subtypeOf(ClassName.get(type_ITollgate))
     )
   );

   // 构建输入参数
   ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();

   // 创建函数 : 'loadInto'
   MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
     .addAnnotation(Override.class)
     .addModifiers(PUBLIC)
     .addParameter(tollgateParamSpec);

   // 遍历拦截器结合,往loadInto函数中添加语句
   if (null != interceptors && interceptors.size() > 0) {
    // Build method body
    for (Map.Entry entry : interceptors.entrySet()) {
     loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));

    // 语句类似于
    // interceptors.put(1, Test1Interceptor.class);
    }
   }

   // 写入文件
   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_ITollgateGroup))
       .build()
   ).build().writeTo(mFiler);

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

verify()

// 验证注解元素是否合格
private boolean verify(Element element) {
  Interceptor interceptor = element.getAnnotation(Interceptor.class);
  return null != interceptor && ((TypeElement)element).getInterfaces().contains(iInterceptor);
 }

通过上面的分析可以得到以下几点:

不能设置相同优先级的拦截器,否则会抛出异常

InterceptorProcessor生成的类文件格式为:ARouter$$Interceptors$$xxx

AutowiredProcessor

@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends AbstractProcessor

init()

 @Override
 public synchronized void init(ProcessingEnvironment processingEnvironment) {
  super.init(processingEnvironment);

  mFiler = processingEnv.getFiler();     // Generate class.
  types = processingEnv.getTypeUtils();   // Get type utils.
  elements = processingEnv.getElementUtils();  // Get class meta.

  typeUtils = new TypeUtils(types, elements);

  logger = new Logger(processingEnv.getMessager()); // Package the log utils.
 }

process()

 // process函数主要关注两点 categories() 和 generateHelper()
 @Override
 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
  if (CollectionUtils.isNotEmpty(set)) {
   try {
    logger.info(">>> Found autowired field, start... <<<");
    // 1. 分组
    categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
    // 2. 
    generateHelper();

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

  return false;
 }

categories

private Map> parentAndChild = new HashMap<>();
// 将注解元素分组
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
  if (CollectionUtils.isNotEmpty(elements)) {
   for (Element element : elements) { // 遍历
    // 获取注解字段所在的类信息
    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
    // 注解的字段不能为private,否则抛出异常
    if (element.getModifiers().contains(Modifier.PRIVATE)) {
     throw new IllegalAccessException("The autowired fields CAN NOT BE 'private'!!! please check field ["
       + element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
    }
    // 判断集合中是否存在集合中
    if (parentAndChild.containsKey(enclosingElement)) { // Has categries
     parentAndChild.get(enclosingElement).add(element);
    } else {
     List childs = new ArrayList<>();
     childs.add(element);
     parentAndChild.put(enclosingElement, childs);
    }
   }

   logger.info("categories finished.");
  }
 }

generateHelper

 // 
 private void generateHelper() throws IOException, IllegalAccessException {
  // ISyringe
  TypeElement type_ISyringe = elements.getTypeElement(ISYRINGE);
  // SerializationService
  TypeElement type_JsonService = elements.getTypeElement(JSON_SERVICE);

  TypeMirror iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
  TypeMirror activityTm = elements.getTypeElement(Consts.ACTIVITY).asType();
  TypeMirror fragmentTm = elements.getTypeElement(Consts.FRAGMENT).asType();
  TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();

  // 构建输入参数
  ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
  // 遍历分组的集合
  if (MapUtils.isNotEmpty(parentAndChild)) {
   for (Map.Entry> entry : parentAndChild.entrySet()) {
    // 构建函数 : 'inject'
    MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
      .addAnnotation(Override.class)
      .addModifiers(PUBLIC)
      .addParameter(objectParamSpec); // 添加参数

    TypeElement parent = entry.getKey();
    List childs = entry.getValue();

    String qualifiedName = parent.getQualifiedName().toString();
    String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));

    // 文件名称例如:Test1Activity$$ARouter$$Autowired
    String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;

    // 
    TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
      .addJavadoc(WARNING_TIPS)
      .addSuperinterface(ClassName.get(type_ISyringe))
      .addModifiers(PUBLIC);
    // 构建SerializationService 字段
    FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
    // 添加字段
    helper.addField(jsonServiceField);
    // inject函数中添加语句
    // serializationService = ARouter.getInstance().navigation(SerializationService.class);

    injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class);", ARouterClass, ClassName.get(type_JsonService));

    // 转换对象
    // 比如:Test1Activity substitute = (Test1Activity)target;
    injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));

    // 遍历注解变量
    for (Element element : childs) {
     Autowired fieldConfig = element.getAnnotation(Autowired.class);
     String fieldName = element.getSimpleName().toString();

     // 判断是否是IProvider类型
     if (types.isSubtype(element.asType(), iProvider)) { 
      // 如果name为空,则通过Type方式
      if ("".equals(fieldConfig.name())) { 

       injectMethodBuilder.addStatement(
         "substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
         ARouterClass,
         ClassName.get(element.asType())
       );
      } else { // 如果name不为空,则通过name方式

       injectMethodBuilder.addStatement(
         "substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation();",
         ClassName.get(element.asType()),
         ARouterClass,
         fieldConfig.name()
       );
      }

      // 是否是必须传值字段,这里加入了if判断
      if (fieldConfig.required()) {
       injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
       injectMethodBuilder.addStatement(
         "throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
       injectMethodBuilder.endControlFlow();
      }
     } else { // It's normal intent value
      String statment = "substitute." + fieldName + " = substitute.";
      boolean isActivity = false;
      // Activity类型时,通过 getIntent() 方式

      if (types.isSubtype(parent.asType(), activityTm)) { 
       isActivity = true;
       statment += "getIntent().";
      }
      // Fragment类型, 使用 getArguments()
       else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { 
       statment += "getArguments().";
      } 
      // 非Activity或者非Fragment,则抛出异常
      else {
       throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
      }

      statment = buildStatement(statment, typeUtils.typeExchange(element), isActivity);

      // 针对SerializationService添加判空操作
      if (statment.startsWith("serializationService.")) { // Not mortals
       injectMethodBuilder.beginControlFlow("if (null != serializationService)");
       injectMethodBuilder.addStatement(
         "substitute." + fieldName + " = " + statment,
         (StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
         ClassName.get(element.asType())
       );
       injectMethodBuilder.nextControlFlow("else");
       injectMethodBuilder.addStatement(
         "$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
       injectMethodBuilder.endControlFlow();
      } else {
       injectMethodBuilder.addStatement(statment, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
      }

      // Validator
      if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) { // Primitive wont be check.
       injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
       injectMethodBuilder.addStatement(
         "$T.e(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
       injectMethodBuilder.endControlFlow();
      }
     }
    }
    // 往类中添加inject() 函数
    helper.addMethod(injectMethodBuilder.build());

    // 写入文件
    JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);

   }

   logger.info(">>> Autowired processor stop. <<<");
  }
 }

AutowiredProcessor生成的java文件举例如下:

public class Test1Activity$$ARouter$$Autowired implements ISyringe {
 private SerializationService serializationService;

 @Override
 public void inject(Object target) {
 serializationService = ARouter.getInstance().navigation(SerializationService.class);;
 Test1Activity substitute = (Test1Activity)target;
 substitute.name = substitute.getIntent().getStringExtra("name");
 substitute.age = substitute.getIntent().getIntExtra("age", 0);
 substitute.girl = substitute.getIntent().getBooleanExtra("boy", false);
 substitute.pac = substitute.getIntent().getParcelableExtra("pac");
 if (null != serializationService) {
  substitute.obj = serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class);
 } else {
  Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
 }
 substitute.url = substitute.getIntent().getStringExtra("url");
 substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
 }
}

总结

至此,ARouter之Compiler SDK中的三种注解处理器都分析完毕!

接下来的文章开始分析API SDK的源码!以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。


网站标题:阿里路由框架ARouter源码解析之Compiler
新闻来源:http://bzwzjz.com/article/psjshp.html

其他资讯

Copyright © 2007-2020 广东宝晨空调科技有限公司 All Rights Reserved 粤ICP备2022107769号
友情链接: 成都企业网站制作 企业网站建设 高端品牌网站建设 手机网站制作 成都网站建设 企业网站设计 成都网站建设公司 成都品牌网站设计 网站建设费用 重庆网站制作 定制网站制作 成都网站制作 定制网站设计 四川成都网站制作 阿坝网站设计 成都网站建设 网站制作 成都网站建设 营销型网站建设 成都网站建设 网站建设公司 企业网站制作