汇总篇章:
前言
前面对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);
小结
以上就是所有注解部分和插件部分的内容了,请按照大纲中的流程来浏览不迷路