1. 定义
- 在 Activity 进行数据传递一般都会通过 getIntent().putxxx()/getxxx() 方法;在 Fragment 中进行数据传递一般都会通过 getArguments().getxxx() 方法,如果传递的字段过多,可能会写很多个取值语句。为了简化这些繁琐的步骤,ARouter 提供了使用注解的方式来完成参数的注入,这是通过 @Autowired 来实现的。
/**
* Annotation for field, which need autowired.
*
* @author zhilong <a href="mailto:zhilong.lzl@alibaba-inc.com">Contact me.</a>
* @version 1.0
* @since 2017/2/20 下午4:26
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// Mark param's name or service name.
String name() default "";
// If required, app will be crash when value is null.
// Primitive type wont be check!
boolean required() default false;
// Description of the field
String desc() default "";
}
- name():定义了属性获取值的 key
- required():定义了当前属性是否是必须的,默认为 false。如果是必须的,但是值为 null,APP 会 crash。基本类型不会做 null 校验。
- desc():字段说明
2. 使用
@Autowired(name = "isForFreeVip")
var isForFreeVip = false
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class KGLoginActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
KGLoginActivity substitute = (KGLoginActivity)target;
// isForFreeVip 不加 @JvmFiled 注解的话,是 private 遍历,无法通过下面的方式访问到,必须通过 setForFreeVip() 去赋值,那在此处就会赋值失败
substitute.isForFreeVip = substitute.getIntent().getBooleanExtra("isForFreeVip", substitute.isForFreeVip);
}
}
- 赋值操作在
inject()
方法中,这个方法的调用链如下:ARouter.inject() -> _ARouter.inject() -> AutowiredServiceImpl.autowire() -> ISyringe.inject(Object)
3. 源码分析
public void inject(Object thiz) {
_ARouter.inject(thiz);
}
static void inject(Object thiz) {
// 去路由表里查找 "/arouter/service/autowired" 的实现类,就是 AutowiredServiceImpl
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
- AutowiredServiceImpl.autowire()
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
// 存储反射实例
private LruCache<String, ISyringe> classCache;
// 存放不需要注入操作的类
private List<String> 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();
}
// 就是编译期生成的 XXX$$ARouter$$Autowired 文件中的 inject 方法
autowiredHelper.inject(instance);
// 将实例对象缓存起来
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
// 如果发生异常,加入黑名单
blackList.add(className); // This instance need not autowired.
}
}
}
4. 为什么 @Autowired 要搭配 @JvmFiled 同时使用?
-
ARouter 对 kotlin 中的 @Autowired 注解赋值,为什么需要对变量用 @JvmFiled 标记?
-
@JvmFiled 的作用:不加 @JvmFiled 默认就会生成 setter() 和 getter() 方法,赋/取值操作实际上是调用 setter() 和 getter(),而变量本身对 java 代码是不可见的,可以认为是 private。使用 @JvmField 标记后,反编译后的 java 代码中发现,不会再自动生成对应的 setter() 和 getter() 方法,此时变量对 java 代码也是可见的。
-
查看源码 com.alibaba.android.arouter.core.AutowiredServiceImpl#autowire()
方法中,通过反射获取变量名并进行赋值,但是反射时并没有设置 setAccessible(true)
【这句话的作用是允许访问私有变量】,不能访问私有变量,从而导致赋值失败
-
去掉 @JvmFiled 反编译后的结果:
@Autowired
private boolean isForFreeVip;
public final boolean isForFreeVip() {
return this.isForFreeVip;
}
public final void setForFreeVip(boolean var1) {
this.isForFreeVip = var1;
}
@JvmField
@Autowired
public boolean isForFreeVip;