ARouter路由插件源码分析

汇总篇章:

ARouter路由源码分析汇总

前言

前面对ARouter的源码进行了分析,为了分析的完整性(洁癖),接下来就对ARouter的插件进行分析,这一篇是基于ARouter的gradle插件及编译的注解生成部分进行源码分析,当然也会通过一些小例子来辅助我们理解这块,顺便提高我们撸代码的逼格

插件启动

在ARouter源码的gradle项目中,arouter-gradle-plugin这个module中用来做gradle插件,我们进入gradle插件的启动类

public class PluginLaunch implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        def isApp = project.plugins.hasPlugin(AppPlugin)
        首先判断在application模块下在进行插件生成注册代码
        if (isApp) {
            Logger.make(project)
            Logger.i('Project enable arouter-register plugin')
            def android = project.extensions.getByType(AppExtension)
            def transformImpl = new RegisterTransform(project)

            初始化arouter的配置信息
            ArrayList<ScanSetting> list = new ArrayList<>(3)
            list.add(new ScanSetting('IRouteRoot'))
            list.add(new ScanSetting('IInterceptorGroup'))
            list.add(new ScanSetting('IProviderGroup'))
            填充目标配置信息集合
            RegisterTransform.registerList = list
            向application中进行插件注册
            android.registerTransform(transformImpl)
        }
    }
}

插件Transform转换

在这里插入图片描述

[getName]:指明transform转换的名称,在ARouter中声明为"com.alibaba.arouter"
[isIncremental]:是否开启增量编译,在ARouter中关闭了增量编译

[getInputTypes]:
	【ContentType的数据类型】: CLASSES和RESOURCES两种
      CLASSES:包含了项目中.class文件和三方库中的.class文件
      RESOURCES:仅包含项目中的.class文件
	 在ARouter中inputType为CLASSES

[getScopes]:要处理的.class文件的范围        
	【Scope的数据类型】: PROJECT、SUB_PROJECTS、EXTERNAL_LIBRARIES、PROJECT_LOCAL_DEPS、SUB_PROJECTS_LOCAL_DEPS等
   在ARouter中getScope为Scope.PROJECT,Scope.PROJECT_LOCAL_DEPS,Scope.SUB_PROJECTS,Scope.SUB_PROJECTS_LOCAL_DEPS,Scope.EXTERNAL_LIBRARIES

[transform]: 即转换的主要逻辑实现
    

接下来就着重分析transform,

  • 第一部分扫描jar文件是:

    在这里插入图片描述

    • 获取jar的绝对路径的md5值

    • 使用OutputProvider输出名为 jar名称_md5 值的文件

    • 对非如下条件的:
      在这里插入图片描述

      进行jar文件扫描

    • ScanUtil的scanJar方法

      在这里插入图片描述

      将编译的jar文件内com/alibaba/android/arouter/routes/所有的内容进行scanClass处理(scanClass放在后面统一分析);

      对于com/alibaba/android/arouter/core/LogisticsCenter.class 在扫描完成后,生成注册代码进目标文件,同步RegisterTransform的fileContainsInitClass状态

      可能有人对这块有些疑问,其实这里就牵扯到前面文章分析的LogisticsCenter中的loadRouterMap方法

      这里就动态的添加registerRouteRoot(目标类)

      在这里插入图片描述

  • 第二部分扫描class文件是:

    在这里插入图片描述

    这里就是扫描所有的class内容,对符合条件的class进行scanClass操作

    在这里插入图片描述

    在scanClass中借助ASM的classVisitor将符合条件的class存放在ScanSetting的list中

  • 最后一部分

    若scanJar方法扫描到LogisticsCenter,则执行更新代码操作

    在这里插入图片描述

    遍历ScanSetting的list去更新loadRouterMap的方法,实现方法在RegisterCodeGenerator中,关键代码如下:

    在这里插入图片描述

    借助ASM的ClassVisitor的visitMethod方法,在RouteMethodVisitor中进行插入代码:

    在这里插入图片描述

    至此插件部分相关的就结束了

Compiler相关

前面赘述了在gradle插件中的工作,接下来就分析ARouter中注解处理部分,在这部分中主要处理三个注解处理类

AutowiredProcessor

这个类中,处理的就是我们在使用过程中@Autowired 的注解,帮我们对我们@Autowired注解修饰的变量进行赋值

在该注解处理器中总共可分为注解分类识别和代码生成两个功能

  • 注解分类识别

    在这里插入图片描述

    在该部分中,将roundEnvironment中获取的所有的Autowired进行分类

    将需要注入的成员变量的相关信息存放在名为parentAndChild的容器内

  • 代码生成generateHelper

    在这里插入图片描述

    首先获取如下类信息:
    com.alibaba.android.arouter.facade.template.ISyringe
    com.alibaba.android.arouter.facade.service.SerializationService
    com.alibaba.android.arouter.facade.template.IProvider
    android.app.Activity
    android.app.Fragment
    android.support.v4.app.Fragment
    

    在这里插入图片描述

    创建一个方法带Override注解名为inject且带有一个Object类型参数的Public方法
    
    创建一个实现了ISyringe接口名为[注解所在类名称$$ARouter$$Autowired]的Public类
    
    

    在这里插入图片描述

    添加一个名为SerializationService的private成员变量
    
    在刚才创建的inject方法中添加如下两行代码:
    	serializationService = ARouter.getInstance().navigation(SerializationService.class);
    	 注解所在类 substitute = (注解所在类)target;
    	 
    
    • 情况一:

      在这里插入图片描述

      遍历当前类内所有带Autowired注解的成员变量
      当成员变量的类型是IProvider时
      如果Autowired注解的name没有赋值则用类型跳转,语句为:
      	substitute.成员变量名称 = ARouter.getInstance().navigation(当前类.class);
      否则添加如下语句:
      	substitute.成员变量名称 = ARouter.getInstance().build(Autowired注解的name).navigation(当前类.class);
      	
      该注解参数是否必传,如果是必传,则在inject添加如下语句:
      	if (substitute.成员变量名称 == null) {
      		throw new RuntimeException("The field 成员变量名称 is null, in class 当前类名称 !");
      	}
      	
      
    • 情况二

      在这里插入图片描述

      buildStatement方法体过长,这里只罗列一部分:

      在这里插入图片描述

      正常进入时
      如果是Activity的,添加:
      	substitute.成员变量名称 = [如果是Serializable类型的则加上类型转换]substitute.getIntent().getBooleanExtra(如果注解的name存在则用name没有则用substitute.成员变量名称)
      如果是fragment的,则添加:
      	substitute.成员变量名称 = [如果是Serializable类型的则加上类型转换]substitute.getArguments().getBoolean(如果注解的name存在则用name没有则用substitute.成员变量名称)
      	
      	
      如果是OBJECT,则statement为serializationService.parseObject
      此时会添加如下代码:
      	if (null != serializationService) {
            substitute.成员变量名称 = serializationService.parseObject(substitute.getIntent().getStringExtra(如果注解的name存在则用name没有则用substitute.成员变量名称), new com.alibaba.android.arouter.facade.model.TypeWrapper<当前归属类>(){}.getType());
          } else {
            Log.e("ARouter::", "You want automatic inject the field 'substitute.成员变量名称' in class 当前归属类 , then you should implement 'SerializationService' to support object auto inject!");
          }
      

      在这里插入图片描述

      与之前的必传项相同,以上就是对AutoProcessor的注解处理

RouteProcessor

核心注解处理类,之所以成为核心注解处理类,ARouter的跳转的关键实现就是这里,废话不多说直接开大

在这里插入图片描述

直接对Route方法进行parse处理

  • 在这里插入图片描述

    获取Activity/Service/fragment/IRouteGroup/IRouteRoot类型
    

    在这里插入图片描述

    创建一个Map<String, Class<? extends IRouteGroup>>和一个Map<String, RouteMeta>
    

    在这里插入图片描述

    构建参数名为:
      Map<String, Class<? extends IRouteGroup>> routes
      Map<String, RouteMeta> atlas
      Map<String, RouteMeta> providers
     
    创建一个带Override名为loadInfo且带有"Map<String, Class<? extends IRouteGroup>> routes"参数的public方法
    

    在这里插入图片描述

    根据element的类型,进行填充routeMeta
    

    在这里插入图片描述

    将RouteMeta放到对应的分组中
    

    在这里插入图片描述

    创建一个带Override名为loadInfo且带有"Map<String, RouteMeta> providers"参数的public方法
    创建一个存放route文档的容器
    
    对分组数据的处理
    //遍历分组数据
    for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
                    String groupName = entry.getKey();
    								//创建一个带Override名为loadInfo且带有"Map<String, RouteMeta> atlas"参数的public方法
                    MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                            .addAnnotation(Override.class)
                            .addModifiers(PUBLIC)
                            .addParameter(groupParamSpec);
    								//创建route信息的容器
                    List<RouteDoc> routeDocList = new ArrayList<>();
    
                    // 按组内的信息存放对应的组内
                    Set<RouteMeta> groupData = entry.getValue();
                    for (RouteMeta routeMeta : groupData) {
                      	//生成对应的routeMeta详细内容
                        RouteDoc routeDoc = extractDocInfo(routeMeta);
    
                        ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
    
                        switch (routeMeta.getType()) {
                            case PROVIDER: 
                            //获取provider类型的父类信息
                                List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
                                for (TypeMirror tm : interfaces) {
                                    routeDoc.addPrototype(tm.toString());
    																	//找到iProvider类型
                                      //在provider中添加如下语句:
                                        //" providers.put(扫描当前类信息, RouteMeta.build(RouteType.PROVIDER, 当前类.class, route路径, 分组名称, 等));"
                                    if (types.isSameType(tm, iProvider)) {
                                        loadIntoMethodOfProviderBuilder.addStatement(
                                                "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                                                (routeMeta.getRawType()).toString(),
                                                routeMetaCn,
                                                routeTypeCn,
                                                className,
                                                routeMeta.getPath(),
                                                routeMeta.getGroup());
                                    } else if (types.isSubtype(tm, iProvider)) {
                                        loadIntoMethodOfProviderBuilder.addStatement(
                                                "providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
                                                tm.toString(),    // 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();
    										//在参数名为atlas的loadinfo方法中添加如下语句:
                        //atlas.put(route路径, RouteMeta.build(routeMeta类型, Test2Activity.class, route路径", "test", new java.util.HashMap<String, Integer>(){{如果有参数}}等));
                        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);
                    }
    

    在这里插入图片描述

    向参数名为routes的loadInto方法中添加
    	routes.put(分组名称, 分组生成类.class);)
    如果生成routeDoc,则输出routeDoc
    
    生成文件

    在这里插入图片描述

    将provider/root写进本地文件
    
    名称为:[ARouter$$Providers$$分组名称]
    
    名称为:[ARouter$$Root$$分组名称]
    
    

    到这里就结束了

InterceptorProcessor

在这里插入图片描述

查找所有的Interceptor注解方法,按照其优先级存放

在这里插入图片描述

创建一个名为[ARouter$$Interceptors$$分组名称]
创建一个带Override名为loadInfo且带有"Map<Integer, Class<? extends IInterceptor>> interceptors"参数的public方法

将扫描到所有interceptor在loadInfo方法中添加:
interceptors.put(interceptor优先级, intercepter所在类.class);

小结

以上就是所有注解部分和插件部分的内容了,请按照大纲中的流程来浏览不迷路

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章