@Autowired
在Activity进行数据传递一般都会通过getIntent().putxxx()/getxxx()方法;在Fragment中进行数据传递一般都会通过getArguments().getxxx()方法,如果传递的字段过多,可能会写很多个取值语句。为了简化这些繁琐的步骤,ARouter提供了使用注解的方式来完成参数的注入,这是通过@Autowired来实现的。
定义
先来看看 @Autowired
的定义。
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// 从intent中获取名为name()的值
String name() default "";
// 如果是非基本元素,且是必须的,会做null校验。
boolean required() default false;
String desc() default "";
}
-
name()
定义了属性获取值的key; -
required()
定义了当前属性是否是必须的,ARouter会做校验;
使用
使用十分简单,只需要在需要取值的字段上使用
@Autowired
修饰即可。
@Autowired(name = "orderId")
long orderId;
然后运行代码,会发现并没有什么卵用。
莫慌,先来看看 build/generated/source/kapt
下生成的文件。
public class MainActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
MainActivity substitute = (MainActivity)target;
substitute.orderId = substitute.getIntent().getLongExtra("orderId", substitute.orderId);
}
}
发现赋值操作主要是通过 inject()
方法完成的,那么这个方法在哪里调用的呢?追溯源码会发现这个方法的调用链是酱紫的 ARouter.inject() -> _ARouter.inject() -> AutowiredServiceImpl.autowire()
。
而且有一个实现了 Application.ActivityLifecycleCallbacks
的类 AutowiredLifecycleCallback.java
在 onActivityCreated()
中调用了 ARouter.getInstance().inject(activity)
。
@Deprecated
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class AutowiredLifecycleCallback implements Application.ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
ARouter.getInstance().inject(activity);
}
// ...
}
但是这个类已经被标记为过时了,所以不考虑这种方式。
那么只能手动去调用 ARouter.getInstance().inject()
。修改完代码,再次运行,很nice。
源码分析
前面说了,它的调用链 ARouter.inject() -> _ARouter.inject() -> AutowiredServiceImpl.autowire()
是这样子的。先来看看 ARouter.inject()
。
public void inject(Object thiz) {
_ARouter.inject(thiz);
}
只是简单的调用了 _ARouter.inject()
,再看看其源码。
static void inject(Object thiz) {
// 这里回去路由表里面查找 /arouter/service/autowired 的实现类,就是AutowiredServiceImpl
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
// 然后调用其 autowire()
autowiredService.autowire(thiz);
}
}
这个方法做了两件事情:
- 先从路由表中查找名为
/arouter/service/autowired
的类,也就是AutowiredServiceImpl
; - 然后调用
AutowiredServiceImpl.autowire()
。
再来看看 AutowiredServiceImpl
的源码。
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
private LruCache classCache;
private List blackList;
@Override
public void init(Context context) {
classCache = new LruCache<>(66);
blackList = new ArrayList<>();
}
@Override
public void autowire(Object instance) {
String className = instance.getClass().getName();
try {
// 不在黑名单
if (!blackList.contains(className)) {
// 从缓存中取
ISyringe autowiredHelper = classCache.get(className);
if (null == autowiredHelper) { // No cache.
// 如果缓存没有,则通过反射的方式,创建实例
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
// 调用其inject()方法,就是编译期生成的xxx$$ARouter$$Autowired文件的inject()
autowiredHelper.inject(instance);
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
// 如果抛异常,则丢到黑名单中
blackList.add(className);
}
}
}
-
classCache
用来存放xx$$ARouter$$Autowired
的反射实例,blackList用来存放不需要注入操作的类; -
autowire()
先看当前类是否在黑名单列表,如果不在,则从缓存中获取当前类对应的生成类的实例; - 如果缓存中没有,则通过反射的方式创建生成类的实例,并调用
inject()
方法。
AutowiredProcessor
上面分析完 @Autowired
的源码,接下来再分析一下,xx$ARouter$$Autowired
类是如何生成的?Annotation Processor
主要是通过 AbstractProcessor.process()
方法做的处理,追溯源码,找到 AutowiredProcessor.java
,接下来就看看 process()
方法究竟干了什么。
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
// 1. 这里会先将所有使用到了@Autowired的元素进行分类
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
// 2. 分类完成之后再进行文件生成操作
generateHelper();
} catch (Exception e) {
}
return true;
}
return false;
}
可以看到 process()
方法分为两步
- 先调用
categories()
将元素进行分类; - 然后调用
generateHelper()
对分类后的元素进行处理,生成对应的java
文件。
再来看看 categories()
方法。
private void categories(Set extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) {
// 1. 获取元素所在类的 TypeElement 对象
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// 2. 这里解释了为什么 @Autowired 注解修饰的 field 不能使用 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 是一个map,key是内部使用了@Autowired注解的TypeElement,value是这个类下面所有使用了@Autowired注解的Element
if (parentAndChild.containsKey(enclosingElement)) {
parentAndChild.get(enclosingElement).add(element);
} else {
List childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
categories()
方法的逻辑十分的简单,接下来看看 generateHelper()
,这个方法有点长,我们来一段一段的分析。
// 构造参数
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (MapUtils.isNotEmpty(parentAndChild)) {
for (Map.Entry> entry : parentAndChild.entrySet()) {
// 构造方法
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
TypeElement parent = entry.getKey();
List childs = entry.getValue();
// 获取类所在包名
String qualifiedName = parent.getQualifiedName().toString();
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
// 指定生成类的名称为 类名$$ARouter$$Autowired
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
// 构造方法
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
// 构造属性
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
helper.addField(jsonServiceField);
// 给方法添加语句
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));
// ... 省略后面的代码
}
这里使用的是 squareup
的开源库 javapoet
。这个库的使用,大家可以看官方文档,这里就不做介绍了。上面这段代码定义了如下的一个类和方法。
// 定义的类
package packageName;
import com.alibaba.android.arouter.facade.service.SerializationService;
public class 类名$$ARouter$$Autowired implement ISyringe {
private SerializationService serializationService;
}
// 定义的方法
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
类名 substitute = (类名)target;
}
继续看后续的代码。
// 这里遍历当前类下所有的@Autowired注解修饰的属性
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
String fieldName = element.getSimpleName().toString();
if (types.isSubtype(element.asType(), iProvider)) {
// 1. 如果是 Provider
} else {
// 2. 如果不是 Provider,就正常的Intent方式
}
}
可以看到在 for
语句中对 Element
的处理主要分为两种;一种是对于 IProvider
的自动注入处理,一种是对于正常 Activity
/ Fragment
进行数据传递的时候自动注入的处理。
先来看看 IProvider
的自动注入。
if ("".equals(fieldConfig.name())) {
// 没有指定path的情况
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else {
// 指定了path的情况
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// 如果是必须的,做一个校验
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();
}
可以看到,对于 IProvider
的自动注入处理逻辑如下:
- 如果
@Autowired
注解没有指定name()
,则通过Class
来查找对应的IProvider
; - 如果指定了
name()
,则直接通过路径来查找对应的IProvider
; - 如果
require()
返回了true
,则给出校验的逻辑。
接下来我们再看看 Activity
/ Fragment
数据自动注入是如何处理的。
String originalValue = "substitute." + fieldName;
// buildCastCode 主要是判断是否是 Serializable和Parcelable,然后对其进行类型转换
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) {
// 如果是 Activity,则使用getIntent() 方式
isActivity = true;
statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {
// 如果是 Fragment,则使用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);
if (statement.startsWith("serializationService.")) {
// 只有当Element对应的类型不是8大基本类型,String,Serializable,Parcelable的时候,才会有这个逻辑
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 {
// 构造语句,可以看到当@Autowired注解name()对应的值是空的时候直接使用属性名作为key获取
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// 如果fieldConfig 是必须的,并且Element 不是8大基本数据类型的时候,加个判断语句
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();
}
buildStatement()
这个方法主要就是根据 Element
对应的类型进行调用 Intent
不同的方法进行取值。
private String buildStatement(String originalValue, String statement, int type, boolean isActivity) {
switch (TypeKind.values()[type]) {
case BOOLEAN:
statement += (isActivity ? ("getBooleanExtra($S, " + originalValue + ")") : ("getBoolean($S)"));
break;
case BYTE:
statement += (isActivity ? ("getByteExtra($S, " + originalValue + ")") : ("getByte($S)"));
break;
case SHORT:
statement += (isActivity ? ("getShortExtra($S, " + originalValue + ")") : ("getShort($S)"));
break;
case INT:
statement += (isActivity ? ("getIntExtra($S, " + originalValue + ")") : ("getInt($S)"));
break;
case LONG:
statement += (isActivity ? ("getLongExtra($S, " + originalValue + ")") : ("getLong($S)"));
break;
case CHAR:
statement += (isActivity ? ("getCharExtra($S, " + originalValue + ")") : ("getChar($S)"));
break;
case FLOAT:
statement += (isActivity ? ("getFloatExtra($S, " + originalValue + ")") : ("getFloat($S)"));
break;
case DOUBLE:
statement += (isActivity ? ("getDoubleExtra($S, " + originalValue + ")") : ("getDouble($S)"));
break;
case STRING:
statement += (isActivity ? ("getExtras() == null ? " + originalValue + " : substitute.getIntent().getExtras().getString($S, " + originalValue + ")") : ("getString($S)"));
break;
case SERIALIZABLE:
statement += (isActivity ? ("getSerializableExtra($S)") : ("getSerializable($S)"));
break;
case PARCELABLE:
statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
break;
case OBJECT:
statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new " + TYPE_WRAPPER + "<$T>(){}.getType())";
break;
}
return statement;
}
还有最重要的一步。
// 给生成类添加方法
helper.addMethod(injectMethodBuilder.build());
// 通过Filer在编译期生成文件
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
至此,AutowiredProcessor.java
的源码就分析完了。
图解
经过上面的分析,大致上可以画出 @Autowired 的基本原理。
- 在编译期通过
AutowiredProcessor
注解处理器生成对应的文件; - 在运行期通过
AutowiredServiceImpl
根据名字反射对应的生成文件,调用inject()
; - 如果未找到文件,则放入
blackList
,若找到则缓存到classCache
;