一、ARouter
ARouter使用的是APT(Annotation Processing Tool)注解处理器,通过给对应的类添加注解,在编译器动态生成对应的路由表文件。这里以分析ARouter的RouteProcessor。在ARouter的使用配置上,需要给base库配置
dependencies {
// Replace with the latest version 现在的最新版本是1.5.1
compile 'com.alibaba:arouter-api:?'
annotationProcessor 'com.alibaba:arouter-compiler:?'
...
}
然后给每个组件都配置annotationProcessor,如果使用kotlin,则使用kapt
接着给每个组件都配置上下面的内容:
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
}
这个配置主要是通过这个annotationProcessorOptions获取到key为AROUTER_MODULE_NAME的值,这个值其实就是module的name,这个的作用就是作为一个Root文件的命名的,因为一个module中可能会有多个group,而多个group归属于一个Root,而ARouter的做法就是将一个module作为一个Root。
1.AbstractProcessor相关类说明
(1)Element
Element 是一个接口,它只在编译期存在和Type有区别,表示程序的一个元素,可以是package,class,interface,method,成员变量,函数参数,泛型类型等。
它的子类包括ExecutableElement, PackageElement, Parameterizable, QualifiedNameable, TypeElement, TypeParameterElement, VariableElement。
Element的子类介绍:
ExecutableElement:表示类或者接口中的方法,构造函数或者初始化器。
PackageElement :表示包程序元素
TypeELement:表示一个类或者接口元素
TypeParameterElement:表示类,接口,方法的泛型类型例如T。
VariableElement:表示字段,枚举常量,方法或者构造函数参数,局部变量,资源变量或者异常参数。
Element只在编译期可见
(2)相关函数
asType(): 返回TypeMirror,TypeMirror是元素的类型信息,包括包名,类(或方法,或参数)名/类型。TypeMirror的子类有ArrayType, DeclaredType, DisjunctiveType, ErrorType, ExecutableType, NoType, NullType, PrimitiveType, ReferenceType, TypeVariable, WildcardType ,getKind可以获取类型。
equals(Object obj): 比较两个Element利用equals方法。
getAnnotation(Class<A> annotationType): 传入注解可以获取该元素上的所有注解。
getAnnotationMirrors(): 获该元素上的注解类型。
getEnclosedElements(): 获取该元素上的直接子元素,类似一个类中有VariableElement。
getEnclosingElement(): 获取该元素的父元素,如果是PackageElement则返回null,如果是TypeElement则返回PackageElement,如果是TypeParameterElement则返回泛型Element
getKind():返回值为ElementKind,通过ElementKind可以知道是那种element,具体就是Element的那些子类。
getModifiers(): 获取修饰该元素的访问修饰符,public,private。
getSimpleName(): 获取元素名,不带包名,如果是变量,获取的就是变量名,如果是定义了int age,获取到的name就是age。如果是TypeElement返回的就是类名。
getQualifiedName():获取类的全限定名,Element没有这个方法它的子类有,例如TypeElement,得到的就是类的全类名(包名)。
2.源码分析—路由注解处理器
(1)BaseProcessor
public abstract class BaseProcessor extends AbstractProcessor {
//文件生成器
Filer mFiler;
Logger logger;
// 用于操作TypeMirror的工具类
Types types;
// 处理Element的工具类
// 还可以用来获取对应的类(通过字符串全路径)获取对应的TypeElement等
Elements elementUtils;
// type工具类,里面有针对TypeMirror的工具Types和对应的序列化类
TypeUtils typeUtils;
// Module name, maybe its 'app' or others
// 从options中取出每个module的name,这个moduleName的值,其实是在每个
// module的build.gradle中进行配置的arguments = [AROUTER_MODULE_NAME: project.getName()]
String moduleName = null;
// If need generate router doc
boolean generateDoc;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
typeUtils = new TypeUtils(types, elementUtils);
logger = new Logger(processingEnv.getMessager());
// Attempt to get user configuration [moduleName]
// 这里就是从每个module中的build.gradle的annotationProcessorOptions配置中取出options数据
// 需要使用组件化的module都需要配置该options,然后这个key对应的name就是moduleName
Map<String, String> options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
}
// 如果module中的build.gradle没有配置annotationProcessorOptions
// 也就是无法取到对应的moduleName,则动态生成对应的moduleName
if (StringUtils.isNotEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
logger.info("The user has configuration the module name, it was [" + moduleName + "]");
} else {
logger.error(NO_MODULE_NAME_TIPS);
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
}
@Override
public SourceVersion getSupportedSourceVersion() {
// 配置源码版本,其实就是JDK版本
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedOptions() {
// 配置options的key
// KEY_MODULE_NAME是一个字符串"AROUTER_MODULE_NAME"
return new HashSet<String>() {{
this.add(KEY_MODULE_NAME);
this.add(KEY_GENERATE_DOC_NAME);
}};
}
}
(2)RouteProcessor
具体的注解处理流程如下:
1.准备条件判断和文件生成需要的类型和类名
2.创建Root和Group文件的方法参数类型名对象
3.定义Root和Group文件的loadInfo方法的参数对象,包括类型和参数名称
4.创建Root类文件的loadInfo方法的构建者对象(这里感觉可以在最后创建)
5.遍历所有元素,对元素进行分组,根据@Route传入的group进行分组,一组对应多个路由
6.定义Provider类文件的loadInfo方法
7.生成所有的Group类文件,这里与传统使用JavaPoet生成Java类文件略有不同,因为使用了接口的方式,所以定义TypeSpec和JavaFile组合进行
8.rootMap添加分组文件信息
9.遍历rootMap集合,添加使用与输出Group类文件同样的方式输出Root类文件
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.
private Map<String, String> rootMap = new TreeMap<>(); // Map of root metas, used for generate class file in order.
private TypeMirror iProvider = null;
private Writer docWriter; // Writer used for write doc
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// 如果需要生成路由器文档,则创建对应的Writer对象
if (generateDoc) {
try {
docWriter = mFiler.createResource(
StandardLocation.SOURCE_OUTPUT,
PACKAGE_OF_GENERATE_DOCS,
"arouter-map-of-" + moduleName + ".json"
).openWriter();
} catch (IOException e) {
logger.error("Create doc writer failed, because " + e.getMessage());
}
}
// 获取IProvider类型
iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@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... <<<");
// 解析路由表
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// prepare the type an so on.
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
// 获取Activity、Service、Fragment和V4包下的Fragment的类型
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
// 获取ARouter相关的接口类型,一个是Root文件类实现的接口
// 这里单独从静态常量中定义成一个TypeElement是因为不止被用于接口实现,还在Root类文件的方法参数中
// 有一个Map集合参数,Map<String, Class<? extends IRouteGroup>> routes这里用到了这个Group类型
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
// RouteMeta的ClassName,用于替换$T.build这里
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
// RouteType的ClassName,用于替换RouteMeta.build($T.ACTIVITY)这里的$T
ClassName routeTypeCn = ClassName.get(RouteType.class);
/**
* TODO:1.创建Root、Group类中的loadInfo方法的参数类型
*/
/*
Build input type, format as :
```Map<String, Class<? extends IRouteGroup>>```
*/
// 这里创建的是Root文件的loadInfo方法的参数类型
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
// Class<? extends IRouteGroup>
// 第一个参数就是Class,泛型因为是使用了extends,所以需要使用WildcardTypeName
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
```Map<String, RouteMeta>```
*/
// 这里创建的是Group文件的loadInfo方法的参数类型
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/**
* TODO:2.定义Root、Group、Provider等类的loadInfo方法的参数
*/
/*
Build input param name.
*/
// 定义要不同文件对应的loadInfo方法的参数名称
// root文件的loadInfo方法的参数名称
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
// group文件的loadInfo方法的参数名称
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
// provider文件的loadInfo方法的参数名称
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/**
* TODO:3.生成对应的Root类的方法的Builder,但是这需要在最后使用
*/
/*
Build method : 'loadInto'
*/
// 生成对应root文件的方法
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
/**
* TODO:4.遍历所有的元素,将元素进行分组
* 分成多个的Group,每个Group有多个路由,即一个Group使用一个Key,对应一个Set集合的路由表
* 最后进行一步排序
*/
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
// 首先找出每个组下对应的metas,然后再找将组统计为root
for (Element element : routeElements) {
// 获取使用Route注解的元素的类型,比如Activity、Fragment、Service、Provider等
TypeMirror tm = element.asType();
// 获取element的Route注解
Route route = element.getAnnotation(Route.class);
// 定义每个element对应的RouteMeta,其实就是保存在Group文件中的loadInfo方法的参数Map集合中的value
RouteMeta routeMeta;
// Activity or Fragment
// 如果该元素是Activity或者Fragment类型
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// Get all fields annotation by @Autowired
// 获取该元素中所有被@Autowired注解的字段
// 用于保存在对应的Activity或者Fragment的封装类RouteMeta中
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
// 找出Activity或者Fragment类型的element中使用@Autowired注解的字段
// 包括其父类中的字段
// Autowired的key是对应的字段名
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
// 这里就给对应的路由设置了对应的类型,比如RouteType.ACTIVITY
// RouteType这个是枚举类型
// route是路由的path,element其实就是被@Route注释的类
// paramsType是所有的参数类型
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
// Fragment
logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
// 如果是IProvider类型
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
// 如果是Service类型
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
// 针对分组对路由进行排序
// 将同组的路由保存在一个Set集合中,并且使用Group名作为key保存到对应的Map集合中
categories(routeMeta);
}
/**
* TODO:5.定义Provider对应的类的loadInfo方法
*/
// 定义Provider的方法
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map<String, List<RouteDoc>> docSource = new HashMap<>();
/**
* TODO:6.生成所有的Group文件
* 并且对所有的Group和GroupFile进行一一对应,保存在Map集合中
*/
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
// 开始生产java文件,分成上下两层。
// 从遍历整个root集合开始。groupMap其实就是整个root对应的所有路由表数据
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
// 获取分组名
String groupName = entry.getKey();
// 定义group文件的方法
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)// 定义对应的方法添加Override注解类
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List<RouteDoc> routeDocList = new ArrayList<>();
// Build group method body
// 取出该group分组中的所有路由,也就是一个分组数据
Set<RouteMeta> groupData = entry.getValue();
// 遍历所有的路由
// TODO:遍历一个Group分组中的所有路由
for (RouteMeta routeMeta : groupData) {
// 获取路由中的额外的文档信息
RouteDoc routeDoc = extractDocInfo(routeMeta);
// 获取路由的真实类型名称
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
// 遍历路由类型,判断是Provider与否
switch (routeMeta.getType()) {
case PROVIDER: // Need cache provider's super class
// 这里的routeMeta.getRawType()其实是一个Element
// 而实现了IProvider的,比如ARouter的demo中的HelloServiceImpl
// 被@Route注解的对应的接口实现类
// 然后获取到该element的所有实现的接口,包括父类的
// 接着遍历其实现的接口,遍历判断是否有实现了IProvider接口
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString());
// TODO: 在创建Provider文件的时候,其内部也会将对应的键值对数据保存在一个Map
// Provider文件其实就是类似Group文件,只不过Provider中的Map的key并不是path
// Provider中的Map的key,其实是直接实现或者继承了IProvider接口的类的全路径
// 如果该element直接实现了IProvider接口
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,// IProvider最终实现类型
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// 如果是element其父类实现了IProvider接口
// 比如HelloServiceImpl就是这个element,而实现了IProvider的是HelloService
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,// IProvider最终实现类型,比如HelloServiceImpl
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);
}
// 这里是每个path类中的使用了@Autowired注解的字段信息
String mapBody = mapBodyBuilder.toString();
// TODO:向Group类中添加一条路由信息
// 定义group文件的loadInfo方法的方法体
// 该group中的每个路由就添加一条方法体
// 即每一条方法体就是一个path信息
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(),// 替换第一个$S 这个字符串
routeMetaCn,// 这是RouteMeta的ClassName,用于替换$T.build中的这个$T
routeTypeCn,// RouteType的ClassName,用于替换build($T中的这个$T 其实就是RouteType.ACTIVITY或者RouteType.FRAGMENT
className,// 使用@Route注解的类的ClassName,替换$T.class中的这个$T
routeMeta.getPath().toLowerCase(),// 该路由的路径
routeMeta.getGroup().toLowerCase());// 该路由所在分组
routeDoc.setClassName(className.toString());
routeDocList.add(routeDoc);
}
// TODO:输出Group分组文件
// Generate groups
// 输出group分组文件。一个分组输出一个分组文件
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))// 定义对应的类实现的接口
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())// 定义对应的类中的方法
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
// TODO:向rootMap中添加分组文件名与分组名的一一对应
// for循环里每次都保存对应的groupFileName到对应的rootMap中
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
/**
* TODO:7.遍历rootMap,即遍历所有的分组
* 向Root类中的loadInfo方法添加方法体,记录每一个Group信息,用于找到Group
*/
if (MapUtils.isNotEmpty(rootMap)) {
// Generate root meta by group name, it must be generated before root, then I can find out the class of group.
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
/**
* TODO:8.输出doc文件
*/
// Output route doc
if (generateDoc) {
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
/**
* TODO:9.输出provider类文件
*/
// Write provider into disk
// 输出provider相关的文件
// Provider文件与Group文件是类似的,在查找的时候通过Autowired注解的对象类型的全路径名
// 从对应的Warehouse.providersIndex缓存中找到对应的RouteMeta对象
// 然后从RouteMeta中取出对应的IProvider接口的最终实现类型
// 这个最终实现类型就是被@Route注解的IProvider接口实现类型
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 + " <<<");
/**
* TODO:10.输出Root类文件
*/
// Write root meta into disk.
// 输出root文件用于找到对应的分组,其实就是保存分组的
// 一个分组中有多个路由表
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
}
/**
* Recursive inject config collector.
*
* @param element current element.
*/
private void injectParamCollector(Element element, Map<String, Integer> paramsType, Map<String, Autowired> injectConfig) {
for (Element field : element.getEnclosedElements()) {
if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {
// It must be field, then it has annotation, but it not be provider.
Autowired paramConfig = field.getAnnotation(Autowired.class);
String injectName = StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name();
paramsType.put(injectName, typeUtils.typeExchange(field));
injectConfig.put(injectName, paramConfig);
}
}
// if has parent?
TypeMirror parent = ((TypeElement) element).getSuperclass();
if (parent instanceof DeclaredType) {
Element parentElement = ((DeclaredType) parent).asElement();
if (parentElement instanceof TypeElement && !((TypeElement) parentElement).getQualifiedName().toString().startsWith("android")) {
injectParamCollector(parentElement, paramsType, injectConfig);
}
}
}
/**
* Extra doc info from route meta
*
* @param routeMeta meta
* @return doc
*/
private RouteDoc extractDocInfo(RouteMeta routeMeta) {
RouteDoc routeDoc = new RouteDoc();
routeDoc.setGroup(routeMeta.getGroup());
routeDoc.setPath(routeMeta.getPath());
routeDoc.setDescription(routeMeta.getName());
routeDoc.setType(routeMeta.getType().name().toLowerCase());
routeDoc.setMark(routeMeta.getExtra());
return routeDoc;
}
/**
* Sort metas in group.
* 将分组的路由信息进行排序,按照path进行排序
* @param routeMete metas.
*/
private void categories(RouteMeta routeMete) {
// 排序的时候,首先沿着路由的合法性
// 其实就是向RouteMeta中设置路由,如果@Route注解的group没有值
// 则从path中取出第一段作为路由
if (routeVerify(routeMete)) {
logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
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() + " <<<");
}
}
/**
* Verify the route meta
* 验证路由的合法性
* @param meta raw meta
*/
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;
}
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;
}
}
3.属性注入注解处理器AutowiredProcessor
首先,看下属性注解处理器生成的文件示例:
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.age = substitute.getIntent().getIntExtra("age", substitute.age);
substitute.height = substitute.getIntent().getIntExtra("height", substitute.height);
substitute.girl = substitute.getIntent().getBooleanExtra("boy", substitute.girl);
substitute.ch = substitute.getIntent().getCharExtra("ch", substitute.ch);
substitute.fl = substitute.getIntent().getFloatExtra("fl", substitute.fl);
substitute.dou = substitute.getIntent().getDoubleExtra("dou", substitute.dou);
substitute.ser = (com.alibaba.android.arouter.demo.service.model.TestSerializable) substitute.getIntent().getSerializableExtra("ser");
substitute.pac = substitute.getIntent().getParcelableExtra("pac");
if (null != serializationService) {
substitute.obj = serializationService.parseObject(substitute.getIntent().getStringExtra("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
if (null != serializationService) {
substitute.objList = serializationService.parseObject(substitute.getIntent().getStringExtra("objList"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestObj>>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'objList' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
if (null != serializationService) {
substitute.map = serializationService.parseObject(substitute.getIntent().getStringExtra("map"), new com.alibaba.android.arouter.facade.model.TypeWrapper<Map<String, List<TestObj>>>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'map' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
substitute.url = substitute.getIntent().getExtras() == null ? substitute.url : substitute.getIntent().getExtras().getString("url", substitute.url);
substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
}
}
(1)AutowiredProcessor源码解析
AutowiredProcessor这个注解处理器的目的,就是通过这个注解处理器给对应的类中的属性进行赋值的操作。
AutowiredProcessor注解处理器流程:
1.首先对资源进行分组,按类分组,一个类包含多个使用了@Autowired注解的字段
2.创建每个注解处理器生成的类中的inject函数的参数,是Object类型,名为target
3.创建每个分组生成的java类对应的函数,方法名是inject
4.初始化创建类的数据,包括创建类需要的包名和类名等信息
5.创建每个分组对应的TypeSpec,这里创建的类需要实现ISyringe接口
6.向类中添加属性,即SerializationService对象,用以解析如Object、List、Map等类型数据
7.创建对应的statement的字符串结构,根据statement字符串进行不同的addStatement操作
8.创建JavaFile,生成对应的文件
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends BaseProcessor {
private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>(); // Contain field need autowired and his super class.
private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
private static final ClassName AndroidLog = ClassName.get("android.util", "Log");
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
logger.info(">>> AutowiredProcessor init. <<<");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
logger.info(">>> Found autowired field, start... <<<");
// 获取所有被@Autowired注解的元素,根据其类元素作为一组,进行分组保存
// 这里的分组,父类和子类是分开的分组,子类中不会包含父类的
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
// 这是生成的Java类需要实现的接口类型
TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
// 这是用于操作对象、列表、map等数据的处理类
TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
// 获取对应的IProvider、Activity、Fragment、Fragment_V4的TypeMirror
// 其实就是对应的类元素的类型信息
TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Build input param name. public void inject(Object target)
// 定义方法的参数类型和名称,即Object类型,参数名是target
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
// 如果分组集合不为null,则遍历所有的分组数据
// 根据每个分组,生成对应的Java文件
if (MapUtils.isNotEmpty(parentAndChild)) {
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
// Build method : 'inject'
// 创建对应的java文件中的方法的Builder,方法名是inject,参数是前面创建的Object类型的名为target
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
// 这里的parent其实就是对应的使用@Autowired注解的字段所在的类
TypeElement parent = entry.getKey();
// childs其实就是该类中所有被@Autowired注解的字段集合
List<Element> childs = entry.getValue();
// 获取该类的全路径名(包名+类名)
String qualifiedName = parent.getQualifiedName().toString();
// 获取该类的包名路径(包名)
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
// 定义该类对应的生成的类的文件名(即生成类的类名)
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");
// 定义要生成的类的TypeSpec的Builder
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);
// 在方法中对该属性进行初始化 serializationService = ARouter.getInstance().navigation(SerializationService.class);
// ARouterClass是com.alibaba.android.arouter.launcher包下的ARouter类
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
// 将inject的参数转成对应的@Autowired注解的属性所在的类类型,比如Test1Activity
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
// Generate method body, start inject.
// 遍历该元素类中的所有被@Autowired注解注解的字段
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
// 获取被@Autowired注解的字段名
String fieldName = element.getSimpleName().toString();
// 判断被@Autowired注解的字段类型是否是IProvider接口子类型
if (types.isSubtype(element.asType(), iProvider)) { // It's provider
// 如果是IProvider的实现类型,则通过ARouter.getInstance().navigation(元素类型)获取
if ("".equals(fieldConfig.name())) { // User has not set service path, then use byType.
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else { // use byName
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// Validator
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
// 如果是正常的Intent的值
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments().";
} else {
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity, isKtClass(parent));
if (statement.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statement,
(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(statement, 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();
}
}
}
helper.addMethod(injectMethodBuilder.build());
// Generate autowire helper
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
}
logger.info(">>> Autowired processor stop. <<<");
}
}
...
/**
* Categories field, find his papa.
*
* @param elements Field need autowired
*/
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
// 如果元素集合不为null,则遍历元素集合
for (Element element : elements) {
// getEnclosingElement() 获取该元素的父元素,如果是PackageElement则返回null,
// 如果是TypeElement则返回PackageElement,如果是TypeParameterElement则返回泛型Element
// 这里是类中的字段,getEnclosingElement会获取父元素,应该就是获取该字段所在的类元素
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// 如果元素使用了private关键字变成私有的,则抛出异常
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
// 根据类元素保存对应的所有被@Autowired注解的字段元素
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {
List<Element> childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
二、ARouter路由原理
ARouter在通过注解处理器生成对应的类文件之后,具体的路由执行操作,都是依赖于ARouter、_ARouter和Postcard等。这里的源码分析,还是基于ARouter的1.5.1的源码进行。
1.首先看具体使用的代码
@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {
@Autowired
int age = 10;
@Autowired
int height = 175;
@Autowired(name = "boy", required = true)
boolean girl;
@Autowired
char ch = 'A';
@Autowired
float fl = 12.00f;
@Autowired
double dou = 12.01d;
@Autowired
TestSerializable ser;
@Autowired
TestParcelable pac;
@Autowired
TestObj obj;
@Autowired
List<TestObj> objList;
@Autowired
Map<String, List<TestObj>> map;
private long high;
@Autowired
String url;
@Autowired
HelloService helloService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
// 通过调用ARouter.getInstance().inject()获取到对应的Test1Activity
// 的Test1Activity$$ARouter$$Autowired,注入对应的属性值
// TODO:这里属性值的注入,还是需要先借助于AutowiredService这个服务来处理ISyringe
ARouter.getInstance().inject(this);
// No more getter ...
// name = getIntent().getStringExtra("name");
// age = getIntent().getIntExtra("age", 0);
// girl = getIntent().getBooleanExtra("girl", false);
// high = getIntent().getLongExtra("high", 0);
// url = getIntent().getStringExtra("url");
String params = String.format(
"name=%s,\n age=%s, \n height=%s,\n girl=%s,\n high=%s,\n url=%s,\n ser=%s,\n pac=%s,\n obj=%s \n ch=%s \n fl = %s, \n dou = %s, \n objList=%s, \n map=%s",
name,
age,
height,
girl,
high,
url,
ser,
pac,
obj,
ch,
fl,
dou,
objList,
map
);
helloService.sayHello("Hello moto.");
((TextView) findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
((TextView) findViewById(R.id.test2)).setText(params);
}
}
上面的是目标Activity,而要跳转到该Activity,则需要通过调用ARouter来进行。
ARouter.getInstance().build("/test/activity1")
.withString("name", "老王")
.withInt("age", 18)
.withBoolean("boy", true)
.withLong("high", 180)
.withString("url", "https://a.b.c")
.withSerializable("ser", testSerializable)
.withParcelable("pac", testParcelable)
.withObject("obj", testObj)
.withObject("objList", objList)
.withObject("map", map)
.navigation();
2.分析路由原理
(1)ARouter.build()方法
ARouter.build方法其实就是创建一个Postcard对象,该对象主要就是用来保存对应的页面调整所需要的参数和数据的。而ARouter的操作基本都是交给_ARouter来进行,所以这里ARouter.build的方法其实也是调用了_ARouter的build方法
看_ARouter.build方法的实现
这里因为我们并没使用PathReplaceService这个路径替换的服务,所以忽略这部分判断,直接返回一个Postcard对象,在Postcard对象中保存path和group
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
(2)Postcard的with方法
Postcard的with方法,以下面三个为例:
Postcard.withString
public Postcard withString(@Nullable String key, @Nullable String value) {
mBundle.putString(key, value);
return this;
}
Postcard.withSerializable
public Postcard withSerializable(@Nullable String key, @Nullable Serializable value) {
mBundle.putSerializable(key, value);
return this;
}
Postcard.withObject
public Postcard withObject(@Nullable String key, @Nullable Object value) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
mBundle.putString(key, serializationService.object2Json(value));
return this;
}
其实Postcard的with方法中,只有withObject比较特殊,其他的都是常规的向Bundle添加数据。在withObject中使用到了SerializationService,其实现类只有一个JsonServiceImpl。
@Route(path = "/yourservicegroupname/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);
}
@Override
public <T> T parseObject(String input, Type clazz) {
return JSON.parseObject(input, clazz);
}
}
从这里我们可以看出,JsonServiceImpl也是使用了路由的,这里可以使用路由的原因,其实就是SerializationService接口继承了IProvider,而ARouter会对实现了IProvider的类进行注解处理,从而也会生成对应的IProvider文件。这类文件主要是用来提供功能支持。所以SerializationService实现类对象可以通过ARouter.getInstance().navigation(SerializationService.class);来获取。
而withObject,其实就是将Object数据转换成Json字符串进行传递
(3)Postcard.navigation()
public Object navigation() {
return navigation(null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context) {
return navigation(context, null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
/**
* Navigation to the route with path in postcard.
*
* @param mContext Activity and so on.
* @param requestCode startActivityForResult's param
*/
public void navigation(Activity mContext, int requestCode) {
navigation(mContext, requestCode, null);
}
/**
* Navigation to the route with path in postcard.
*
* @param mContext Activity and so on.
* @param requestCode startActivityForResult's param
*/
public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}
Postcard中的navigation方法,有多个重载,可以支持使用requestCode,如果是无参的navigation()的话,则是使用applicationContext,其他的则需要自己传入context。
Postcard的navigation方法,其实最终就是调用到了_ARouter.navigation()方法
ARouter.navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback)
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
(4)_ARouter.navigation
_ARouter.navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
// 这里是将路由和分组添加到一个缓存中
// 下次取用的时候直接从这个缓存中获取,而不需要再去创建对应的生成的java文件对象
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
if (null != callback) {
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(postcard, requestCode, callback);
}
return null;
}
最后具体的跳转页面的实现,则是在_ARouter._navigation方法中。
(5)LogisticsCenter.completion
这里其实就是缓存对应的路由,将对应的path和RouteMeta通过调用对应的路由类进行缓存,避免多次创建路由类对象。
并且添加对应的目标Class等信息到Postcard中。
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
// Warehouse.groupsIndex是在LogisticsCenter调用init方法初始化的时候添加分组信息缓存的
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
// 加载路由信息到缓存中
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
addRouteGroupDynamic(postcard.getGroup(), null);
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() + "]");
}
// 重新加载的目的就是为了在路由缓存中无数据的时候
// 先加载缓存,然后再根据path将对应的路由信息加入到postcard中
completion(postcard); // Reload
}
} else {
// 添加目标Class
postcard.setDestination(routeMeta.getDestination());
// 添加目标类型,比如ACTIVITY、FRAGMENT、SERVICE
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
// 添加Extra
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
// Its provider, so it must implement IProvider
// 如果是Provider,则是从Provider文件中取出对应的RouteMeta对象
// 而在RouteMeta对象中,会保存对应的Provider.class
// 取出Provider.class进行初始化,保存在Warehouse.providers中
// 接着保存在对应的Postcard中,这个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;
}
}
}
(6)LogisticsCenter.addRouteGroupDynamic
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Warehouse.groupsIndex.containsKey(groupName)){
// If this group is included, but it has not been loaded
// load this group first, because dynamic route has high priority.
// 如果分组信息缓存中存在对应的分组
// 那么就初始化该分组生成的对应Group类文件,然后向路由缓存中添加路由信息
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
// 这里的目的,是因为ARouter可以动态添加路由信息
// 即采用主动的接口实现类的方式,实现IRouteGroup接口,然后做路由添加的操作
if (null != group) {
// 这里就是调用group分组的loadInfo方法,向Warehouse.routes缓存中添加对应的键值对
// key是path,value是RouteMeta
group.loadInto(Warehouse.routes);
}
}
(7)_ARouter._navigation
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class<?> fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
(8)LogisticsCenter.init
在LogisticsCenter.init初始化函数,是由_Arouter.init中进行初始化的,而_ARouter.init是在ARouter.init中进行初始化的。在LogisticsCenter.init中就会遍历所有的Root、Group、Provider、Interceptor文件,将对应的查找数据添加到缓存中,然后每次对实际的group、path对应的数据的查找都是从缓存中进行查找。
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
遍历对应的生成的文件,是在ClassUtils.getFileNameByPackageName中进行的,将生成的对应的文件类名保存在Set集合中,然后遍历该集合,判断是Root、Interceptor、Provider,做分别的缓存处理。
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的集合
*/
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
// 这里使用双重循环,比较耗费性能。
// 所以使用了arouter-gradle-plugin插件,使用字节码插桩的方式,节约性能
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
3.IProvider实现类寻找
IProvider接口实现对象的赋值,其实也是通过@Autowired注解,而要获取IProvider接口实现对象,则是通过
ARouter.getInstance().navigation(HelloService.class);
这里最终是执行到_ARouter的一个navigation方法
(1)_ARouter.navigation(Class<? extends T> service)
protected <T> T navigation(Class<? extends T> service) {
try {
// 从Provider缓存中取出IProvider接口路径对应的RouteMeta
// 然后再根据该RouteMeta对象创建Postcard
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
// Compatible 1.0.5 compiler sdk.
// Earlier versions did not use the fully qualified name to get the service
if (null == postcard) {
// No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}
if (null == postcard) {
return null;
}
// Set application to postcard.
postcard.setContext(mContext);
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
这里最终还是调用到了LogisticsCenter.completion方法,在该方法中,会向Postcard对象中传入对应的IProvider接口实现类对象的初始化,用于被@Autowired注解的对象的赋值,也可以直接获取到该IProvider接口实现类对象,进行使用。IProvider接口实现类对象不能在页面跳转的时候传递,但是可以通过common定义对应的接口,然后在对应的组件实现该接口,比如可以在common中定义HelloService,在personal组件实现HelloServiceImpl,HelloServiceImpl使用@Route注解,接着可以在比如news组件通过HelloService找到该HelloServiceImpl进行使用。
三、ARouter的初始化
在Application的onCreate中调用
if (BuildConfig.DEBUG) { // These two lines must be written before init, otherwise these configurations will be invalid in the init process
ARouter.openLog(); // Print log
ARouter.openDebug(); // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
}
ARouter.init(this); // As early as possible, it is recommended to initialize in the Application
这里ARouter.init()就是初始化整个路由表的分组、拦截器分组、服务分组等,保存在Warehouse中的不同的静态Map集合中,并且使用分组名称作为对应的key。
这里其实就是全局保存了每个分组的Class
获取到ARouter注解处理器生成的对应的ClassName,然后反射创建Root文件类对象,接着调用loadInfo方法,将@Route路由值作为key,对应的对应的Group、Interceptor、Service分别保存在不同的静态Map集合中。
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//billy.qi modified at 2017-12-06
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler.
// 取出对应的Root的className集合
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
// 遍历Root文件的className集合,反射创建Root对象
// 然后调用loadInfo方法将分组Group的Class保存在Warehouse中的Map集合
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
而Group中的具体的RouteMeta则是在调用navigation()方法的时候,加入到Warehouse.routes集合中,RouteMeta中的信息一般是:具体的类的Class、path路径、group分组等
Class就是用来在startActivity的时候用的,如果是Service的话,就是用来反射创建对象。