Android开源系列-组件化框架Arouter-(三)APT技术详解
theme: juejinhighlight: a11y-light携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情 >> Hi,我是小余。本文已收录到 GitHub · Androider-Planet 中。这里有 Android 进阶成长知识体系,关注公众号 [小余的自习室] ,在成功的路上不迷路!前言最近组里需要进行组件化框架的改造,用到了ARouter这个开源框架,为了更好的对项目进行改造,笔者花了一些时间去了解了下ARouterARouter是阿里巴巴团队在17年初发布的一款针对组件化模块之间无接触通讯的一个开源框架,经过多个版本的迭代,现在已经非常成熟了。ARouter主要作用:组件间通讯,组件解耦,路由跳转,涉及到我们常用的Activity,Provider,Fragment等多个场景的跳转接下来笔者会以几个阶段来对Arouter进行讲解:Android开源系列-组件化框架Arouter-(一)使用方式详解Android开源系列-组件化框架Arouter-(二)深度原理解析Android开源系列-组件化框架Arouter-(三)APT技术详解Android开源系列-组件化框架Arouter-(四)AGP插件详解这篇文章我们来讲解下:APT技术详解APT前置知识注解基础:1.元注解1.@Target:目标,表示注解修饰的目标ElementType.ANNOTIONS_TYPE: 目标是注解,给注解设置的注解ElementType.CONSTRUCTOR: 构造方法ElementType.FIELD: 属性注解ElementType.METHOD: 方法注解ElementType.Type: 类型如:类,接口,枚举ElementType.PACKAGE: 可以给一个包进行注解ElementType.PARAMETER: 可以给一个方法内的参数进行注解ElementType.LOCAL_VARIABLE: 可以给局部变量进行注解2.@Retention:表示需要在什么级别保存该注解信息RetentionPolicy.SOURCE:在编译阶段有用,编译之后会被丢弃,不会保存到字节码class文件中RetentionPolicy.CLASS:注解在class文件中可用,但是会被VM丢弃,在类加载时会被丢弃,在字节码文件处理中有用,注解默认使用这种方式RetentionPolicy.RUNTIME:运行时有效,可以通过反射获取注解信息3.@Document:将注解包含到javaDoc中4.@Inherit:运行子类继承父类的注解5.@Repeatable:定义注解可重复2.元注解的使用方式2.1:基本使用方式@Target(ElementType.METHOD) ://表示作用在方法中
@Retention(RetentionPolicy.SOURCE) ://表示只在编译器有效
public @interface Demo1 {
public int id(); //注解的值,无默认值,在创建注解的时候需要设置该值
public String desc() default "no info";//注解默认值
}
@Demo1(id=1)
public void getData() {
}2.2:重复注解使用方式定义Persons:@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();
}定义Person:@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}使用使用:@Person(role="CEO")
@Person(role="husband")
@Person(role="father")
@Person(role="son")
public class Man {
String name="";
}调用注解if(Man.class.isAnnotationPresent(Persons.class)) {先判断是否存在这个注解
Persons p2=Man.class.getAnnotation(Persons.class);获取注解
for(Person t:p2.value()){
System.out.println(t.role());
}
}
结果:
1
CEO
husband
father
son3.运行时注解需要使用反射获取@Retention(RetentionPolicy.RUNTIME)
public void getAnnoInfo() {
Class clazz = GetAnno.class;
//获得所有的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
method.setAccessible(true);//禁用安全机制
if (method.isAnnotationPresent(Demo1.class)) {//检查是否使用了Demo1注解
Demo1 demo1 = method.getAnnotation(Demo1.class);//获得注解实例
String name = method.getName();//获得方法名称
}
}4.编译时注解需要使用到APT工具@Retention(RetentionPolicy.SOURCE)或者CLASS注解的获取可以使用编译期注解动态生成代码,很多优秀的开源库都是使用这个方式:如Arouter ButterKnife,GreenDao,EventBus3等APT知识储备1.APT是一种注解解析工具:**在编译期找出源代码中所有的注解信息,如果指定了注解器(继承AbstractProcessor),那么在编译期会调用这个注解器里面的代码,我们可以在这里面做一些处理,如根据注解信息动态生成一些代码,并将代码注入到源码中**使用到的工具类:工具类1:Element表示程序的一个元素,它只在编译期存在。可以是package,class,interface,method,成员变量,函数参数,泛型类型等。Element的子类介绍:ExecutableElement:类或者接口中的方法,构造器或者初始化器等元素PackageElement:代表一个包元素程序VariableElement:代表一个类或者接口中的属性或者常量的枚举类型,方法或者构造器的参数,局部变量,资源变量或者异常参数TypeElement:代表一个类或者接口元素TypeParameterElement:代表接口,类或者方法的泛型参数元素通过Element可以获取什么信息呢?1.asType() 返回TypeMirror:
TypeMirror是元素的类型信息,包括包名,类(或方法,或参数)名/类型
TypeMirror的子类:
ArrayType, DeclaredType, DisjunctiveType, ErrorType, ExecutableType, NoType, NullType, PrimitiveType, ReferenceType, TypeVariable, WildcardType
getKind可以获取类型:
2.equals(Object obj) 比较两个Element利用equals方法。
3.getAnnotation(Class annotationType) 传入注解可以获取该元素上的所有注解。
4.getAnnotationMirrors() 获该元素上的注解类型。
5.getEnclosedElements() 获取该元素上的直接子元素,类似一个类中有VariableElement。
6.getEnclosingElement() 获取该元素的父元素,
如果是属性VariableElement,则其父元素为TypeElement,
如果是PackageElement则返回null,
如果是TypeElement则返回PackageElement,
如果是TypeParameterElement则返回泛型Element
7.getKind() 返回值为ElementKind,通过ElementKind可以知道是那种element,具体就是Element的那些子类。
8.getModifiers() 获取修饰该元素的访问修饰符,public,private
9.getSimpleName() 获取元素名,不带包名,
如果是变量,获取的就是变量名,
如果是定义了int age,获取到的name就是age。
如果是TypeElement返回的就是类名
10.getQualifiedName():获取类的全限定名,Element没有这个方法它的子类有,例如TypeElement,得到的就是类的全类名(包名)。
11.Elements.getPackageOf(enclosingElement).asType().toString():获取所在的包名:
工具类2:ProcessingEnvironment:APT运行环境:里面提供了写新文件, 报告错误或者查找其他工具.1.getFiler():返回用于创建新的源,类或辅助文件的文件管理器。
2.getElementUtils():返回对元素进行操作的一些实用方法的实现.
3.getMessager():返回用于报告错误,警告和其他通知的信使。
4.getOptions():返回传递给注解处理工具的处理器特定选项。
5.getTypeUtils():返回一些用于对类型进行操作的实用方法的实现。
工具类3:ElementKind如何判断Element的类型呢,需要用到ElementKind,ElementKind为元素的类型,元素的类型判断不需要用instanceof去判断,而应该通过getKind()去判断对应的类型element.getKind()==ElementKind.CLASS;
工具类4:TypeKindTypeKind为类型的属性,类型的属性判断不需要用instanceof去判断,而应该通过getKind()去判断对应的属性element.asType().getKind() == TypeKind.INT
javapoet:生成java文件3种生成文件的方式:1.StringBuilder·进行拼接2.模板文件进行字段替换3.javaPoet 生成StringBuilder进行拼接,模板文件进行字段替换进行简单文件生成还好,如果是复杂文件,拼接起来会相当复杂所以一般复杂的都使用Square出品的sdk:javapoetimplementation "com.squareup:javapoet:1.11.1"自己实现自定义APT工具类步骤:1.创建一个单独javalib模块lib_annotions:创建需要的注解类:@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}2.再创建一个javalib模块lib_compilers:在模块中创建一个继承AbstractProcessor的类:@AutoService(Processor.class)
public class CustomProcessorTest extends AbstractProcessor {
public Filer filer;
private Messager messager;
private List<String> result = new ArrayList<>();
private int round;
private Elements elementUtils;
private Map<String, String> options;
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotations = new LinkedHashSet<>();
annotations.add(CustomBindAnnotation.class.getCanonicalName());
return annotations;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
messager = processingEnvironment.getMessager();
elementUtils = processingEnv.getElementUtils();
options = processingEnv.getOptions();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
messager.printMessage(Diagnostic.Kind.NOTE,"process");
Map<TypeElement, Map<Integer, VariableElement>> typeElementMap = getTypeElementMap(roundEnv);
messager.printMessage(Diagnostic.Kind.NOTE,"2222");
for(TypeElement key:typeElementMap.keySet()){
Map<Integer, VariableElement> variableElementMap = typeElementMap.get(key);
TypeSpec typeSpec = generalTypeSpec(key,variableElementMap);
String packetName = elementUtils.getPackageOf(key).getQualifiedName().toString();
messager.printMessage(Diagnostic.Kind.NOTE,"packetName:"+packetName);
JavaFile javaFile = JavaFile.builder(packetName,typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
messager.printMessage(Diagnostic.Kind.NOTE,"3333");
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private TypeSpec generalTypeSpec(TypeElement key,Map<Integer, VariableElement> variableElementMap) {
return TypeSpec.classBuilder(key.getSimpleName().toString()+"ViewBinding")
.addModifiers(Modifier.PUBLIC)
.addMethod(generalMethodSpec(key,variableElementMap))
.build();
}
private MethodSpec generalMethodSpec(TypeElement typeElement, Map<Integer, VariableElement> variableElementMap) {
ClassName className = ClassName.bestGuess(typeElement.getQualifiedName().toString());
String parameter = "_" + toLowerCaseFirstChar(className.simpleName());
MethodSpec.Builder builder = MethodSpec.methodBuilder("bind")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.returns(void.class)
.addParameter(className,parameter);
messager.printMessage(Diagnostic.Kind.NOTE,"typeElement.getQualifiedName().toString():"+typeElement.getQualifiedName().toString());
messager.printMessage(Diagnostic.Kind.NOTE,"typeElement.className():"+className.simpleName().toString());
messager.printMessage(Diagnostic.Kind.NOTE,"parameter:"+parameter);
for(int viewId:variableElementMap.keySet()){
VariableElement variableElement = variableElementMap.get(viewId);
String elementName = variableElement.getSimpleName().toString();
String elementType = variableElement.asType().toString();
messager.printMessage(Diagnostic.Kind.NOTE,"elementName:"+elementName);
messager.printMessage(Diagnostic.Kind.NOTE,"elementType:"+elementType);
// builder.addCode("$L.$L = ($L)$L.findViewById($L);\n",parameter,elementName,elementType,parameter,viewId);
builder.addStatement("$L.$L = ($L)$L.findViewById($L)",parameter,elementName,elementType,parameter,viewId);
}
// for (int viewId : varElementMap.keySet()) {
// VariableElement element = varElementMap.get(viewId);
// String name = element.getSimpleName().toString();
// String type = element.asType().toString();
// String text = "{0}.{1}=({2})({3}.findViewById({4}));";
// builder.addCode(MessageFormat.format(text, parameter, name, type, parameter, String.valueOf(viewId)));
// }
return builder.build();
}
private Map<TypeElement, Map<Integer, VariableElement>> getTypeElementMap(RoundEnvironment roundEnv) {
Map<TypeElement, Map<Integer, VariableElement>> typeElementMap = new HashMap<>();
messager.printMessage(Diagnostic.Kind.NOTE,"1111");
Set<? extends Element> variableElements = roundEnv.getElementsAnnotatedWith(CustomBindAnnotation.class);
for(Element element:variableElements){
VariableElement variableElement = (VariableElement) element;//作用在字段上,可以强制转换为VariableElement
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
Map<Integer, VariableElement> varElementMap = typeElementMap.get(typeElement);
if(varElementMap == null){
varElementMap = new HashMap<>();
typeElementMap.put(typeElement,varElementMap);
}
CustomBindAnnotation customBindAnnotation = variableElement.getAnnotation(CustomBindAnnotation.class);
int viewId = customBindAnnotation.value();
varElementMap.put(viewId,variableElement);
}
return typeElementMap;
}
//将首字母转为小写
private static String toLowerCaseFirstChar(String text) {
if (text == null || text.length() == 0 || Character.isLowerCase(text.charAt(0))) return text;
else return String.valueOf(Character.toLowerCase(text.charAt(0))) + text.substring(1);
}
}这个类中:重写以下方法1.getSupportedAnnotationTypes:
该方法主要作用是:返回支持的注解类型
public Set<String> getSupportedAnnotationTypes() {
Set<String> hashSet = new HashSet<>();
hashSet.add(BindView.class.getCanonicalName());
return hashSet;
}
2.getSupportedSourceVersion:
作用:返回支持的jdk版本
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
3.init(ProcessingEnvironment processingEnvironment)
作用:返回一个ProcessingEnvironment
这个工具内部有很多处理类
1.getFiler():返回用于创建新的源,类或辅助文件的文件管理器。
2.getElementUtils():返回对元素进行操作的一些实用方法的实现.
3.getMessager():返回用于报告错误,警告和其他通知的信使。
4.getOptions():返回传递给注解处理工具的处理器特定选项。
5.getTypeUtils():返回一些用于对类型进行操作的实用方法的实现。
4.process(Set<? extends TypeElement> set, RoundEnvironment environment):
作用:apt核心处理方法,可以在这里面对收集到的注解进行处理,生成动态原文件等3.在模块的build.gradle文件中implementation "com.google.auto.service:auto-service:1.0-rc6" //使用Auto-Service来自动注册APT
//Android Plugin for Gradle >= 3.4 或者 Gradle Version >=5.0 都要在自己的annotation processor工程里面增加如下的语句
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation "com.squareup:javapoet:1.11.1"//辅助生成文件的工具类
implementation project(':lib_annotionss')//该模块是注解存再的库中4.最后编译会自动生成对应的类。然后在需要的地方加上注解就可以了。编译器自动生成的文件:public class AnnotationActivityViewBinding {
public static void bind(AnnotationActivity _annotationActivity) {
_annotationActivity.btn1 = (android.widget.Button)_annotationActivity.findViewById(2131296347);
_annotationActivity.lv = (android.widget.ListView)_annotationActivity.findViewById(2131296475);
_annotationActivity.btn = (android.widget.Button)_annotationActivity.findViewById(2131296346);
}
}ARouter中APT的使用我们来看ARouter源码框架app:是ARouter提供的一个测试Demoarouter-annotation:这个lib模块中声明了很多注解信息和一些枚举类arouter-api:ARouter的核心api,转换过程的核心操作都在这个模块里面arouter-compiler:APT处理器,自动生成路由表的过程就是在这里面实现的arouter-gradle-plugin:这是一个编译期使用的Plugin插件,主要作用是用于编译器自动加载路由表,节省应用的启动时间。我们主要看arouter-annotation和arouter-compiler这两个模块1.arouter-annotation可以看到这里面实现了几个注解类Autowired:属性注解@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// 标志我们外部调用使用的key
String name() default "";
// 如果有要求,一定要传入,不然app会crash
// Primitive type wont be check!
boolean required() default false;
// 注解字段描述
String desc() default "";
}@Target({ElementType.FIELD}):指定我们注解是使用在属性字段上@Retention(RetentionPolicy.CLASS):指定我们注解只在编译期存在Interceptor:拦截器注解@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
}@Target({ElementType.TYPE}):指定注解是在类上@Retention(RetentionPolicy.CLASS):指定注解在编译期存在Route:路由注解@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
/**
* Path of route
*/
String path();
/**
* Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!!
*/
String group() default "";
/**
* Name of route, used to generate javadoc.
*/
String name() default "";
/**
* Extra data, can be set by user.
* Ps. U should use the integer num sign the switch, by bits. 10001010101010
*/
int extras() default Integer.MIN_VALUE;
/**
* The priority of route.
*/
int priority() default -1;
}@Target({ElementType.TYPE}):指定注解是使用在类上@Retention(RetentionPolicy.CLASS):指定注解是在编译期存在枚举类:RouteType:路由类型public enum RouteType {
ACTIVITY(0, "android.app.Activity"),
SERVICE(1, "android.app.Service"),
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
BOARDCAST(-1, ""),
METHOD(-1, ""),
FRAGMENT(-1, "android.app.Fragment"),
UNKNOWN(-1, "Unknown route type");
}
TypeKind
public enum TypeKind {
// Base type
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
// Other type
STRING,
SERIALIZABLE,
PARCELABLE,
OBJECT;
}model类RouteMeta:路由元数据public class RouteMeta {
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
private int priority = -1; // The smaller the number, the higher the priority
private int extra; // Extra data
private Map<String, Integer> paramsType; // Param type
private String name;
private Map<String, Autowired> injectConfig; // Cache inject config.
}
总结下arouter-annotation:1.创建了Autowired:属性注解,Interceptor:拦截器注解,Route:路由注解2.创建了RouteType:路由类型枚举,RouteMeta:路由元数据2.arouter-compilerAutowiredProcessor:属性Autowired注解处理器InterceptorProcessor:拦截器Interceptor注解处理器RouteProcessor:路由Route注解处理器BaseProcessor:注解处理器基类,主要获取一些通用参数,上面三个都继承这个基类incremental.annotation.processors:拦截器声明,这里将我们需要使用的几个注解处理器做了声明com.alibaba.android.arouter.compiler.processor.RouteProcessor,aggregating
com.alibaba.android.arouter.compiler.processor.AutowiredProcessor,aggregating
com.alibaba.android.arouter.compiler.processor.InterceptorProcessor,aggregating下面依次来看:AutowiredProcessor:@AutoService(Processor.class)//使用AutoService可以将处理器自动注册到processors文件中
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED}) //设置需要匹配的注解类:"com.alibaba.android.arouter.facade.annotation.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 {
//这里将所有声明Autowired注解的属性包括在parentAndChild中:parentAndChild的key为注解的类TypeElement
//parentAndChild{List<Element>{element1,element2,element3...}}
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
//生成帮助类
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
//获取com.alibaba.android.arouter.facade.template.ISyringe的TypeElement
TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
//获取com.alibaba.android.arouter.facade.service.SerializationService的TypeElement
TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
//获取com.alibaba.android.arouter.facade.template.IProvider的TypeMirror:元素的类型信息,包括包名,类(或方法,或参数)名/类型
TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
//获取android.app.Activity的TypeMirror:元素的类型信息,包括包名,类(或方法,或参数)名/类型
TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
//获取android.app.Fragment的TypeMirror:元素的类型信息,包括包名,类(或方法,或参数)名/类型
TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// 生成属性参数的辅助类
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (MapUtils.isNotEmpty(parentAndChild)) {
//遍历parentAndChild:每个entry使用的key为当前类的TypeElement,value为当前类内部所有使用注解Autowired标记的属性
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
//MethodSpec生成方法的辅助类 METHOD_INJECT = 'inject'
/**
方法名:inject
方法注解:Override
方法权限:public
方法参数:前面objectParamSpec生成的:Object target
*/
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
//key为当前类的TypeElement
TypeElement parent = entry.getKey();
//value为当前类内部所有使用注解Autowired标记的属性
List<Element> childs = entry.getValue();
//类的全限定名
String qualifiedName = parent.getQualifiedName().toString();
//类的包名
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
//类的文件名:NAME_OF_AUTOWIRED = $$ARouter$$Autowired,完整fileName = BaseActivity$$ARouter$$Autowired
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
//TypeSpec生成类的辅助类
/**
类名:BaseActivity$$ARouter$$Autowired
类doc:"DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER."
父类:com.alibaba.android.arouter.facade.template.ISyringe
权限:public
*/
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
//生成字段属性辅助类
/**
字段类型:SerializationService
字段名:serializationService
字段属性:private
*/
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
//将字段添加到类:BaseActivity$$ARouter$$Autowired中
helper.addField(jsonServiceField);
/**
给inject方法添加语句:这里parent = BaseActivity
1.serializationService = ARouter.getInstance().navigation(SerializationService.class);
2.BaseActivity substitute = (BaseActivity)target;
*/
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
/**
生成方法内部代码,注入属性
*/
for (Element element : childs) {
//获取当前element注解Autowired的属性:
Autowired fieldConfig = element.getAnnotation(Autowired.class);
//获取注解的名称
String fieldName = element.getSimpleName().toString();
//判断是否是iProvider的子类,说明iProvider字段如果使用Autowired注解的话,会单独处理
if (types.isSubtype(element.asType(), iProvider)) { // It's provider
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 这里如果设置了required为true,则一定要有值,否则会报错
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
//普通属性
/**
假设fieldName = "name"
originalValue = "substitute.name"
statement = "substitute.name = substitute."
*/
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
//判断是Activity 则statement += "getIntent()."
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent().";
//判断是Fragment 则statement += "getArguments()."
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments().";
} else {
//非Activity和Fragment,其他情况抛异常
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
//statement = "substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras()
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 {
//将statement注入到injectMethodBuilder方法中
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// 添加null判断
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());
//生成java文件
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)) {
for (Element element : elements) {
//获取element的父元素:如果是属性,父元素就是类或者接口:TypeElement
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
//如果element属性是PRIVATE,则直接报错,所以对于需要依赖注入的属性,一定不能为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() + "]");
}
//判断parentAndChild是否包含enclosingElement,第一次循环是空值会走到else分支,第二次才会包含
//格式:parentAndChild{List<Element>{element1,element2,element3...}}
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.");
}
}
}
通过在编译器使用注解处理器AutowiredProcessor处理后,自动生成了以下文件BaseActivity$$ARouter$$Autowired.java/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class BaseActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
BaseActivity substitute = (BaseActivity)target;
substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
}
}生成过程:1.使用Map<TypeElement, List> parentAndChild = new HashMap<>()存储所有被Autowired注解的属性key:每个类的TypeElementvalue:当前类TypeElement中所有的Autowired注解的属性字段2.使用ParameterSpec生成参数3.使用MethodSpec生成方法:METHOD_INJECT = 'inject'方法名:inject
方法注解:Override
方法权限:public
方法参数:前面objectParamSpec生成的:Object target4.使用TypeSpec生成类:类名:BaseActivity$$ARouter$$Autowired
类doc:"DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER."
父类:com.alibaba.android.arouter.facade.template.ISyringe
权限:public5.使用addStatement给方法添加语句body6.将方法注入到帮助类中 helper.addMethod(injectMethodBuilder.build()); 7.写入java文件JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler); RouteProcessor@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
//这里表示我们的RouteProcessor可以处理Route和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);
//这里如果支持generateDoc,则打开docWriter,待写入文件:generateDoc字段由模块中的build.gradle文件传入
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的类型TypeMirror
iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
//核心处理api
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<<");
//解析Routes
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的TypeMirror
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
//获取Service的TypeMirror
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
//获取Fragment的TypeMirror
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
//获取IRouteGroup的TypeElement
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
////获取IProviderGroup的TypeElement
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
//获取RouteMeta的ClassName:权限定名
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
//获取RouteType的ClassName:权限定名
ClassName routeTypeCn = ClassName.get(RouteType.class);
/*创建Map<String, Class<? extends IRouteGroup>>类型的ParameterizedTypeName
Build input type, format as :
```Map<String, Class<? extends IRouteGroup>>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*创建Map<String, RouteMeta>类型的ParameterizedTypeName
```Map<String, RouteMeta>```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*创建参数类型rootParamSpec,groupParamSpec,providerParamSpec
Build input param name.
*/
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/*创建loadInto方法的MethodSpec
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
//遍历routeElements所有的path注解对象
for (Element element : routeElements) {
//获取对象element的TypeMirror
TypeMirror tm = element.asType();
//获取element的注解Route
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
// Activity or Fragment 如果是Activity或者Fragment:根据不同情况创建不同的routeMeta路由元数据
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// Get all fields annotation by @Autowired
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
//这里是收集所有的Autowired属性参数
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
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
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_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() + "].");
}
//收集路由元数据
categories(routeMeta);
}
//创建IProvider注解的loadInto方法
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map<String, List<RouteDoc>> docSource = new HashMap<>();
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
//创建IGroupRouter的loadInto方法
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List<RouteDoc> routeDocList = new ArrayList<>();
// 创建 group 方法的 body
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
RouteDoc routeDoc = extractDocInfo(routeMeta);
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
switch (routeMeta.getType()) {
//创建PROVIDER的loadInto方法体
case PROVIDER: // Need cache provider's super class
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString());
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,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// 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,
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();
//创建IGroupRouter的方法体
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);
}
// Generate groups 生成IGroupRrouter的子类文件
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
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()));
}
}
// Output route doc
if (generateDoc) {
//将path关系写入doc
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
// Write provider into disk 写入provider
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 + " <<<");
// Write root meta into disk.写入root meta
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 + " <<<");
}
}
}生成过程:和上面生成AutoWried过程类似,都是使用javapoet的api生成对应的java文件这里我们需要生成三种文件:ARouter$$Root$$xxx:xxx是当前模块名的缩写,存储当前模块路由组的信息:value是路由组的类名 /**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$modulejava implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("m2", ARouter$$Group$$m2.class);
routes.put("module", ARouter$$Group$$module.class);
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
}
}ARouter$$Group$$xxx:xxx是当前路由组的组名,存储一个路由组内路由的信息:内部包含多个路由信息/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}ARouter$$Providers$$xxx,xxx是模块名,存储的是当前模块中的IProvider信息,key是IProvider的名称,value是RouteMeta路由元数据/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Providers$$modulejava implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}还有其他比如拦截器的java文件生成方式就不再描述了,和前面两个注解处理器是一样的原理。自动生成了这些帮助类之后,在编译器或者运行期,通过调用这些类的loadInto方法,可以将路由元信息加载到内存中。总结本文在开始主要讲解一些注解和注解处理器的前置知识,且带大家自己实现了一个APT自动生成文件的demo,最后讲解下在ARouter中APT是如何再编译器动态生成几种帮助类的。到这里已经是ARouter的第三篇了持续输出中。。你的关注和点赞是我最大的动力
Android开源系列-组件化框架Arouter-(二)深度原理解析
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情 >> Hi,我是小余。本文已收录到 GitHub · Androider-Planet 中。这里有 Android 进阶成长知识体系,关注公众号 [小余的自习室] ,在成功的路上不迷路!前言最近组里需要进行组件化框架的改造,用到了ARouter这个开源框架,为了更好的对项目进行改造,笔者花了一些时间去了解了下ARouterARouter是阿里巴巴团队在17年初发布的一款针对组件化模块之间无接触通讯的一个开源框架,经过多个版本的迭代,现在已经非常成熟了。ARouter主要作用:组件间通讯,组件解耦,路由跳转,涉及到我们常用的Activity,Provider,Fragment等多个场景的跳转接下来笔者会以几个阶段来对Arouter进行讲解:Android开源系列-组件化框架Arouter-(一)使用方式详解Android开源系列-组件化框架Arouter-(二)深度原理解析Android开源系列-组件化框架Arouter-(三)APT技术详解Android开源系列-组件化框架Arouter-(四)AGP插件详解这篇文章我们来讲解下:Arouter的基本原理1.ARouter认知首先我们从命名来看:ARouter翻译过来就是一个路由器。官方定义:一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦那么什么是路由呢?简单理解就是:一个公共平台转发系统工作方式:1.注册服务:将我们需要对外暴露的页面或者服务注册到ARouter公共平台中2.调用服务:调用ARouter的接口,传入地址和参数,ARouter解析传入的地址和参数转发到对应的服务中通过ARouter形成了一个无接触解耦的调用过程2.ARouter架构解析我们来看下ARouter的源码架构:app:是ARouter提供的一个测试Demoarouter-annotation:这个lib模块中声明了很多注解信息和一些枚举类arouter-api:ARouter的核心api,转换过程的核心操作都在这个模块里面arouter-compiler:APT处理器,自动生成路由表的过程就是在这里面实现的arouter-gradle-plugin:这是一个编译期使用的Plugin插件,主要作用是用于编译器自动加载路由表,节省应用的启动时间。3.原理讲解这里我们不会一开始就大篇幅对源码进行讲解:我们先来介绍ARouter中的几个重要概念:有了这几个概念,后面在去看源码就会轻松多了前置基础概念:概念1:PostCard(明信片)既然是明信片要将信件寄到目的人的手上就至少需要:收件人的姓名和地址,寄件人以及电话和地址等ARouter就是使用PostCard这个类来存储寄件人和收件人信息的。public final class Postcard extends RouteMeta {
// Base
private Uri uri; //如果使用Uri方式发起luyou
private Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!
private Bundle mBundle; // 需要传递的参数使用bundle存储
private int flags = 0; // 启动Activity的标志:如NEW_FALG
private int timeout = 300; // 路由超时
private IProvider provider; // 使用IProvider的方式跳转
private boolean greenChannel; //绿色通道,可以不经过拦截器
private SerializationService serializationService; //序列化服务serializationService:需要传递Object自定义类型对象,就需要实现这个服务
private Context context; // May application or activity, check instance type before use it.
private String action; //Activity跳转的Action
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
...
}
PostCard继承了RouteMeta:public class RouteMeta {
private RouteType type; // 路由类型:如Activity,Fragment,Provider等
private Element rawType; // 路由原始类型,在编译时用来判断
private Class<?> destination; // 目的Class对象
private String path; // 路由注册的path
private String group; // 路由注册的group分组
private int priority = -1; // 路由执行优先级,priority越低,优先级越高,这个一般在拦截器中使用
private int extra; // Extra data
private Map<String, Integer> paramsType; // 参数类型,例如activity中使用@Autowired的参数类型
private String name; //路由名字,用于生成javadoc
private Map<String, Autowired> injectConfig; // 参数配置(对应paramsType).
}RouteMeta:主要存储的是一些目的对象的信息,这些对象是在路由注册的时候才会生成。概念2:Interceptor拦截器了解OkHttp的都知道,其内部调用过程就是使用的拦截器模式,每个拦截器执行的对应的任务。而ARouter中也是如此,所有的路由调用过程在到达目的地前都会先经过自定义的一系列拦截器,实现一些AOP切面编程。public interface IInterceptor extends IProvider {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
IInterceptor是一个接口,继承了IProvider,所以其也是一个服务类型只需要实现process方法就可以实现拦截操作。概念3:greenChannel:绿色通道设置了绿色通道的跳转过程,可以不经过拦截器概念4:Warehouse:路由仓库Warehouse意为仓库,用于存放被 @Route、@Interceptor注释的 路由相关的信息,也就是我们关注的destination等信息举个例子:moduleB发起路由跳转到moduleA的activity,moduleB没有依赖moduleA,只是在moduleA的activity上增加了@Route注解。 由于进行activity跳转需要目标Activity的class对象来构建intent,所以必须有一个中间人,把路径"/test/activity"翻译成Activity的class对象,然后moduleB才能实现跳转。(因此在ARouter的使用中 moduleA、moduleB 都是需要依赖 arouter-api的)这个中间人那就是ARouter了,而这个翻译工所作用到的词典就是Warehouse,它存着所有路由信息。class Warehouse {
//所有IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级
//(IRouteGroup实现类是编译时生成,代表一个组,即path第一级相同的所有路由,包括Activity和Provider服务)
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
//所有路由元信息,是在completion中赋值,key是path
//首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务
static Map<String, RouteMeta> routes = new HashMap<>();
//所有服务provider实例,在completion中赋值,key是IProvider实现类的class
static Map<Class, IProvider> providers = new HashMap<>();
//所有provider服务的元信息(实现类的class对象),是在ARouter初始化中赋值,key是IProvider实现类的全类名。
//主要用于使用IProvider实现类的class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
static Map<String, RouteMeta> providersIndex = new HashMap<>();
//所有拦截器实现类的class对象,是在ARouter初始化时收集到,key是优先级
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");
//所有拦截器实例,是在ARouter初始化完成后立即创建
static List<IInterceptor> interceptors = new ArrayList<>();
...
}Warehouse存了哪些信息呢?groupsIndex:存储所有路由组元信息:key:group的名称
value:路由组的模块类class类:
赋值时机:初始化的时候routes:存储所有路由元信息。切记和上面路由组分开,路由是单个路由,路由组是一批次路由key:路由的path
value:路由元信息
赋值时机:LogisticsCenter.completion中赋值
备注:首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务providers:存储所有服务provider实例。key:IProvider实现类的class
value:IProvider实例
赋值时机:在LogisticsCenter.completion中赋值providersIndex:存储所有provider服务元信息(实现类的class对象)。key:IProvider实现类的全类名
value:provider服务元信息
赋值时机:ARouter初始化中赋值。
备注:用于使用IProvider实现类class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)interceptorsIndex:存储所有拦截器实现类class对象。key:优先级
value:所有拦截器实现类class对象
赋值时机:是在ARouter初始化时收集到
interceptors,所有拦截器实例。是在ARouter初始化完成后立即创建其中groupsIndex、providersIndex、interceptorsIndex是ARouter初始化时就准备好的基础信息,为业务中随时发起路由操作(Activity跳转、服务获取、拦截器处理)做好准备。概念5:APT注解处理器ARouter使用注解处理器,自动生成路由帮助类:我们使用ARouter编译后,会在对应模块下自动生成以下类:这些类的生成规则都是通过APT在编译器自动生成的,关于APT在ARouter中的使用方式,后面会单独拿一节出来讲解:Android开源系列-组件化框架Arouter-(三)APT技术详解概念6:AGP插件ARouter使用了一个可选插件:"com.alibaba:arouter-register:1.0.2"使用这个插件可以在编译器在包中自动检测以及加载路由表信息,而不需要在运行启动阶段再使用包名去dex文件中加载,提高app启动效率关于这块的,后面会在:Android开源系列-组件化框架Arouter-(四)AGP插件详解有了以上几个概念做基础现在我们再到源码中去看看ARouter是如何跨模块运行起来的源码分析:首先我们来看路由过程:步骤1:初始化ARouterARouter.init(this)步骤2:注册Activity路由@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {
@Autowired
int age = 10;
protected void onCreate(Bundle savedInstanceState) {
ARouter.getInstance().inject(this);
}
}步骤3:通过path启动对应的ActivityARouter.getInstance().build("/test/activity2").navigation();下面我们分别来分析以上过程:步骤1分析:ARouter.init(this)/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
调用了_ARouter同名init方法,进入看看protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}内部初始化了一些mContext,mHandler以及字段信息最重要的是LogisticsCenter.init(mContext, executor):这句进入看看:/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
try {
//使用AGP插件进行路由表的自动加载
loadRouterMap();
//如果registerByPlugin被设置为true,说明使用的是插件加载,直接跳过
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//如果是false,则调用下面步骤加载
Set<String> routerMap;
// 如果是debug模式或者是新版本的,则每次都会去加载routerMap,这会是一个耗时操作
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>()));
}
//这里循环获取routerMap中的信息
for (String className : routerMap) {
//如果className = "com.alibaba.android.arouter.routes.ARouter$$Root"格式,则将路由组信息添加到Warehouse.groupsIndex中
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);
//如果className = "com.alibaba.android.arouter.routes.ARouter$$Interceptors"格式,则将拦截器信息添加到Warehouse.interceptorsIndex中
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
//如果className = "com.alibaba.android.arouter.routes.ARouter$$Providers"格式,则将服务Provider信息添加到Warehouse.providersIndex中
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}总结_ARouter的init操作:1.优先使用插件加载路由表信息到仓库中,如果没有使用插件,则使用包名com.alibaba.android.arouter.routes去dex文件中查找对应的类对象查找到后,保存到sp文件中,非debug或者新版本的情况下,下次就直接使用sp文件中缓存的类信息即可。2.查找到对应的类文件后,使用反射调用对应的类的loadInto方法,将路由组,拦截器以及服务Provider信息加载到Warehouse仓库中继续看init方法中给的_ARouter.afterInitstatic void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}找到/arouter/service/interceptor注解处@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService
这里给ARouter创建了一个InterceptorServiceImpl服务的实例对象,后面讲到拦截器的时候会用到步骤2分析:注册Activity路由我们注册的Activity,Provider等路由信息,会在编译器被注解处理器处理后生成对应的路由表:路由表在步骤1中ARouter初始化的时候被加载到Warehouse中步骤3分析:通过path启动对应的ActivityARouter.getInstance().build("/test/activity2").navigation();这里我们拆分成三个部分:getInstance,build,navigation3.1:getInstancepublic static ARouter getInstance() {
if (!hasInit) {
throw new InitException("ARouter::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (ARouter.class) {
if (instance == null) {
instance = new ARouter();
}
}
}
return instance;
}
}做了init检查并创建了一个ARouter对象3.2:buildpublic Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
调用了_ARouter的同名build方法
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
1.使用PathReplaceService,可以替换原path为新的path
继续看build方法:
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);
}
}看到这里创建了一个Postcard,传入path和group,对Postcard前面有讲解,这里不再重复3.3:navigation最后会走到_ARouter中的同名navigation方法中: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;
}
try {
//完善PostCard信息 留个点1
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();
}
});
}
//没有找到路由信息,则直接返回callback.onLost
if (null != callback) {
callback.onLost(postcard);
} else {
// 没有callback则调用全局降级服务DegradeService的onLost方法
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//回调callback.onFound提醒用户已经找到path
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 {
//绿色通道直接调用_navigation
return _navigation(postcard, requestCode, callback);
}
return null;
}方法任务:1.预处理服务2.完善PostCard信息3.如果是非绿色通道,则使用拦截器处理请求4.调用_navigation处理这里我们看下第3点:拦截器处理interceptorService.doInterceptions{
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
}如果被拦截回调callback.onInterrupt如果没有就执行_navigation方法进入interceptorService.doInterceptions看下:前面分析过interceptorService是InterceptorServiceImpl对象@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
checkInterceptorsInitStatus();
if (!interceptorHasInit) {
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
//使用CancelableCountDownLatch计数器
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_execute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
//拦截器处理超时
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
//拦截器过程出现异常
callback.onInterrupt((Throwable) postcard.getTag());
} else {
//继续执行下面任务onContinue
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
counter.countDown();
//递归调用_execute执行拦截器
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
// Last interceptor execute over with fatal exception.
postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.
counter.cancel();
// Be attention, maybe the thread in callback has been changed,
// then the catch block(L207) will be invalid.
// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }
}
});
}
}
}拦截器总结:1.使用计数器对拦截器技术,执行开始计数器+1,执行结束计数器-1,如果拦截器执行时间到,计数器数大于0,则说明还有未执行完成的拦截器,这个时候就超时了退出2.拦截器执行使用递归的方式进行3.拦截器执行完成继续执行_navigation方法我们来看_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;
}
这个方法其实就是根据PostCard的type来处理不同的请求了1.Activity,直接跳转2.Fragment,Provider,BroadcaseReceiver和ContentProvider,直接返回类的实例对象。整个过程我们就基本了解了。上面还留了一个点:留的点1:ARouter是如何完善PostCard信息看LogisticsCenter.completion(postcard);进入这个方法:public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//去Warehouse.routes去取路由元数据,开始肯定是没有的
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
//没获取到
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
//判断Warehouse.groupsIndex路由组中是否有这个group
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
try {
//动态添加路由元信息到路由中
addRouteGroupDynamic(postcard.getGroup(), null);
} catch (Exception e) {
}
//重新加载。这个时候就会有路由元信息了
completion(postcard); // Reload
}
} else {
//给postcard设置目的地,设置类型,设置优先级,设置Extra等信息
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
...
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
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) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
进入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.
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}看上面代码可知:**数据完善过程是通过组名group去groupsIndex获取对应的组的class对象,然后调用class对象的loadInto方法,将路由元数据加载到Warehouse.routes然后重新调用completion完善方法去Warehouse.routes中取出路由信息并加载到PostCard中,这样PostCard中就获取到了目的地址信息。**下面我画了一张图描述了上面的调用过程一图胜千言总结本文先介绍了ARouter使用过程中 的一些基本概念,理解了这些概念后,我们再从使用步骤触发,对每个使用节点进行了介绍。最后使用一张图总结了整个使用原理过程:这里我们还有一些悬念:1.ARouter帮助类是如何生成的,这里使用到了APT注解处理器的技术关于APT我们会在下一章:Android开源系列-组件化框架Arouter-(三)APT技术详解这里还有个有趣的现象,我们在调用路由表加载的时候:使用了loadRouterMap加载,但是查看里面代码:private static void loadRouterMap() {
registerByPlugin = false;
// auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
}居然是空的。。呃呃呃没关系看注解:auto generate register code by gradle plugin: arouter-auto-register可以看到这里使用了arouter-auto-register插件中自动生成注册代码的方式:这里其实就是使用到了字节码插庄技术,动态添加了代码,这里留到:Android开源系列-组件化框架Arouter-(四)AGP插件详解好了,本篇就到这里了。> 持续输出中。。你的点赞,关注是我最大的动力。
Android开源系列-组件化框架Arouter-(一)使用方式详解
Hi,我是小余。本文已收录到 GitHub · Androider-Planet 中。这里有 Android 进阶成长知识体系,关注公众号 [小余的自习室] ,在成功的路上不迷路!前言:最近组里需要进行组件化框架的改造,用到了Arouter这个开源框架,为了更好的对项目进行改造,笔者花了一些时间去了解了下ArouterARouter是阿里巴巴团队在17年初发布的一款针对组件化模块之间无接触通讯的一个开源框架,经过多个版本的迭代,现在已经非常成熟了。ARouter主要作用:组件间通讯,组件解耦,路由跳转,涉及到我们常用的Activity,Provider,Fragment等多个场景的跳转接下来笔者会以几个阶段来对Arouter进行讲解:Android开源系列-组件化框架Arouter-(一)使用方式详解Android开源系列-组件化框架Arouter-(二)基本原理详解Android开源系列-组件化框架Arouter-(三)APT技术详解Android开源系列-组件化框架Arouter-(三)AGP插件详解这篇文章是Arouter的第一篇:基本使用方式前期准备我们先来新建一个项目:项目架构如下:app:app模块build.gradle:dependencies {
...
implementation 'com.alibaba:arouter-api:1.5.2'
implementation project(':module_java')
implementation project(':module_kotlin')
//annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
}引入arouter的核心api和引用需要调用的模块即可这里如果需要使用依赖注入的方式查找class对象,则需要添加声明注解处理器:annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'module_java:java语言lib模块build.gradle:android {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
dependencies {
...
implementation 'com.alibaba:arouter-api:1.5.2'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
}1.引入arouter-api核心api和arouter-compiler注解处理器api2.设置主机处理器的外部参数AROUTER_MODULE_NAME和AROUTER_GENERATE_DOC,这两个参数会在编译器生成Arouter路由表的时候使用到module_kotlin:kotli语言lib模块build.gradle:apply plugin: 'kotlin-kapt'
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'
}这里的设置方式和java一样,区别:java使用关键字annotationProcessor,而kotlin使用kapt。同步项目后。就可以使用Arouter的api啦。ARouter api1.初始化Arouter建议在application中初始化:ARouter.init(this);2.日志开启ARouter.openLog();3.打开调试模式ARouter.openDebug();调试模式不是必须开启,但是为了防止有用户开启了InstantRun,忘了开调试模式,导致无法使用Demo,如果使用了InstantRun,必须在初始化之前开启调试模式,但是上线前需要关闭,InstantRun仅用于开发阶段,线上开启调试模式有安全风险,可以使用BuildConfig.DEBUG来区分环境4.Activity模块的调用:4.1:注解声明@Route(path = "/gtest/test")
//或者@Route(path = "/gtest/test",group = "gtest")
public class ArouterTestActivity extends AppCompatActivity {
}4.2:组件跨模块调用ARouter.getInstance().build("/gtest/test").navigation();记住:如果注解没有指明group,arouter默认会以path的第一个节点gtest为组名进行分组5.属性依赖注入5.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(this);
}
}
5.2:注解跨模块调用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();传入的Object需要使用Serializable序列化public class TestSerializable implements Serializable {
public String name;
public int id;
public TestSerializable() {
}
public TestSerializable(String name, int id) {
this.name = name;
this.id = id;
}
}注意点:1.常用数据类型直接使用:Autowired注解即可2.如果需要指定外部传入的数据key:可以使用name指定,如果这个字段是必须传入的,则需要指定required = true,外部未传入可能会报错3.传入自定义类的属性则需要先对自定义进行序列化,且需要指定一个SerializationService:这个类是用于Object对象和json进行转化用@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);
}
}
6.Service模块的调用6.1:声明接口,其他组件通过接口来调用服务public interface HelloService extends IProvider {
String sayHello(String name);
}
6.2:实现接口@Route(path = "/yourservicegroupname/hello", name = "测试服务")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello, " + name;
}
@Override
public void init(Context context) {
}
}6.3:使用接口6.3.1:使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType1.使用class类型查找helloService3 = ARouter.getInstance().navigation(HelloService.class);
helloService3.sayHello("Vergil");2.使用path查找helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
helloService4.sayHello("Vergil");6.3.2:(推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取@Autowired
HelloService helloService;
@Autowired(name = "/yourservicegroupname/hello")
HelloService helloService2;
helloService.sayHello("mikeaaaa");
helloService2.sayHello("mikeaaaa");7.Fragment和BOARDCAST以及CONTENT_PROVIDER使用方式和Activity类似,但是这三者都是直接获取对应的Class类对象,拿到对象后,可以操作对象,这里不再讲解8.拦截器的使用拦截器就是在跳转的过程中,设置的对跳转的检测和预处理等操作。8.1:拦截器声明可以添加拦截器的优先级,优先级越高,越优先执行@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
if ("/test/activity4".equals(postcard.getPath())) {
// 这里的弹窗仅做举例,代码写法不具有可参考价值
final AlertDialog.Builder ab = new AlertDialog.Builder(postcard.getContext());
ab.setCancelable(false);
ab.setTitle("温馨提醒");
ab.setMessage("想要跳转到Test4Activity么?(触发了\"/inter/test1\"拦截器,拦截了本次跳转)");
ab.setNegativeButton("继续", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onContinue(postcard);
}
});
ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onInterrupt(null);
}
});
ab.setPositiveButton("加点料", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
postcard.withString("extra", "我是在拦截器中附加的参数");
callback.onContinue(postcard);
}
});
MainLooper.runOnUiThread(new Runnable() {
@Override
public void run() {
ab.create().show();
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* Do your init work in this method, it well be call when processor has been load.
*
* @param context ctx
*/
@Override
public void init(Context context) {
Log.e("testService", Test1Interceptor.class.getName() + " has init.");
}
}可以设置绿色通道模式:greenChannel()不做拦截器处理9.监听跳转状态.navigation(this, new NavCallback() {
@Override
public void onArrival(Postcard postcard) {
Log.d("ARouter", "onArrival");
Log.d("ARouter", "成功跳转了");
}
@Override
public void onInterrupt(Postcard postcard) {
super.onInterrupt(postcard);
Log.d("ARouter", "被拦截了");
}
@Override
public void onFound(Postcard postcard) {
super.onFound(postcard);
Log.d("ARouter", "onFound:path:"+postcard.getPath());
Log.d("ARouter", "找到了对应的path");
}
@Override
public void onLost(Postcard postcard) {
super.onLost(postcard);
Log.d("ARouter", "onLost:path:"+postcard.getPath());
Log.d("ARouter", "没找到了对应的path");
}
});10.自定义全局降级策略// 实现DegradeService接口,并加上一个Path内容任意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
// do something.
}
@Override
public void init(Context context) {
}
}11.动态注册路由信息适用于部分插件化架构的App以及需要动态注册路由信息的场景,可以通过 ARouter 提供的接口实现动态注册 路由信息,目标页面和服务可以不标注 @Route 注解,注意:同一批次仅允许相同 group 的路由信息注册ARouter.getInstance().addRouteGroup(new IRouteGroup() {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/dynamic/activity", // path
RouteMeta.build(
RouteType.ACTIVITY, // 路由信息
TestDynamicActivity.class, // 目标的 Class
"/dynamic/activity", // Path
"dynamic", // Group, 尽量保持和 path 的第一段相同
0, // 优先级,暂未使用
0 // Extra,用于给页面打标
)
);
}
});12:使用自己的日志工具打印日志ARouter.setLogger();13.使用自己提供的线程池ARouter.setExecutor();14.重写跳转URL// 实现PathReplaceService接口,并加上一个Path内容任意的注解即可
@Route(path = "/xxx/xxx") // 必须标明注解
public class PathReplaceServiceImpl implements PathReplaceService {
/**
* For normal path.
*
* @param path raw path
*/
String forString(String path) {
return path; // 按照一定的规则处理之后返回处理后的结果
}
/**
* For uri type.
*
* @param uri raw uri
*/
Uri forUri(Uri uri) {
return url; // 按照一定的规则处理之后返回处理后的结果
}
}15.使用agp插件动态路由表加载1.在项目级build.gradle中:buildscript {
dependencies {
classpath "com.alibaba:arouter-register:$arouter_register_version"
}
}2.在模块级app下引入路由表自动加载插件id 'com.alibaba.arouter'同步代码后,在编译期会自动进行路由表的加载,可以加快arouter的第一次初始化时间,为什么是第一次呢,因为arouter初始化会缓存路由表信息到文件中。Q&A1."W/ARouter::: ARouter::No postcard![ ]"这个Log正常的情况下也会打印出来,如果您的代码中没有实现DegradeService和PathReplaceService的话,因为ARouter本身的一些功能也依赖 自己提供的Service管理功能,ARouter在跳转的时候会尝试寻找用户实现的PathReplaceService,用于对路径进行重写(可选功能),所以如果您没有 实现这个服务的话,也会抛出这个日志推荐在app中实现DegradeService、PathReplaceService2."W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group xxx"通常来说这种情况是没有找到目标页面,目标不存在如果这个页面是存在的,那么您可以按照下面的步骤进行排查a.检查目标页面的注解是否配置正确,正确的注解形式应该是 (@Route(path="/test/test"), 如没有特殊需求,请勿指定group字段,废弃功能)b.检查目标页面所在的模块的gradle脚本中是否依赖了 arouter-compiler sdk (需要注意的是,要使用apt依赖,而不是compile关键字依赖)c.检查编译打包日志,是否出现了形如 ARouter::�Compiler >>> xxxxx 的日志,日志中会打印出发现的路由目标d.启动App的时候,开启debug、log(openDebug/openLog), 查看映射表是否已经被扫描出来,形如 D/ARouter::: LogisticsCenter has already been loaded, GroupIndex[4],GroupIndex > 03.开启InstantRun之后无法跳转(高版本Gradle插件下无法跳转)?因为开启InstantRun之后,很多类文件不会放在原本的dex中,需要单独去加载,ARouter默认不会去加载这些文件,因为安全原因,只有在开启了openDebug之后 ARouter才回去加载InstantRun产生的文件,所以在以上的情况下,需要在init之前调用openDebug4.TransformException:java.util.zip.ZipException: duplicate entry ....ARouter有按组加载的机制,关于分组可以参考 6-1 部分,ARouter允许一个module中存在多个分组,但是不允许多个module中存在相同的分组,会导致映射文件冲突5.Kotlin类中的字段无法注入如何解决?首先,Kotlin中的字段是可以自动注入的,但是注入代码为了减少反射,使用的字段赋值的方式来注入的,Kotlin默认会生成set/get方法,并把属性设置为private 所以只要保证Kotlin中字段可见性不是private即可,简单解决可以在字段上添加 @JvmField6.通过URL跳转之后,在intent中拿不到参数如何解决?需要注意的是,如果不使用自动注入,那么可以不写 ARouter.getInstance().inject(this),但是需要取值的字段仍然需要标上 @Autowired 注解,因为 只有标上注解之后,ARouter才能知道以哪一种数据类型提取URL中的参数并放入Intent中,这样您才能在intent中获取到对应的参数7.新增页面之后,无法跳转?ARouter加载Dex中的映射文件会有一定耗时,所以ARouter会缓存映射文件,直到新版本升级(版本号或者versionCode变化),而如果是开发版本(ARouter.openDebug()), ARouter 每次启动都会重新加载映射文件,开发阶段一定要打开 Debug 功能参考Arouter官方文档