前言
在上一篇中我们分析了ARouter无参数页面跳转的实现方式,今天我们来看看有参数传递的页面跳转是如何实现的,今天的分析基于上一篇的源码,上一篇分析过的内容本篇不再进行重复分析,感兴趣的读者请自行移步Android开源框架系列-组件化之ARouter Activity页面无参数跳转源码分析,先了解基本的页面跳转逻辑。
我们以如下示例代码开始今天的分析。
scss
//页面跳转
ARouter.getInstance().build("/kotlin/test").withString("name", "老王").withInt("age", 23).navigation();
//参数接收
ARouter.getInstance().inject(this)
withString、withInt
build、navigation等方法的实现我们已经分析过,所以我们直接看不同的点,ARouter传递参数和intent的putString、putInt有点类似,我们来看一下:
less
public Postcard withString(@Nullable String key, @Nullable String value) {
mBundle.putString(key, value);
return this;
}
public Postcard withInt(@Nullable String key, int value) {
mBundle.putInt(key, value);
return this;
}
上一篇分析build方法时,我们提到过,build方法的返回值是一个Postcard类型的对象,所以withString、withInt方法是Postcard内的方法,Postcard内部有一个mBundle成员变量,它就是android的Bundle类的对象,可以看到当withString、withInt方法调用的时候,就是将参数存入了bundle中。
参数存入之后执行到navigation方法,然后通过如下的方式存入intent,再通过startActivity方法打开目标页面。
ini
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
startActivity(requestCode, currentContext, intent, postcard, callback);
所以参数传递的源码就到此为止了,那么页面是如何接收这些参数的?既然参数本质上还是通过intent传递的,那么我们推测一下,接收的逻辑是不是最终还是通过intent?
inject
我们开始分析参数接收的逻辑,ARouter.getInstance().inject(this)方法是在目标Activity页面调用的,所以传入的this对象其实就是目标Activity。
在分析inject方法之前,注意到在目标Activity中有这么几个变量定义:
less
@Autowired
@JvmField var name: String? = null
@Autowired
@JvmField var age: Int? = 0
也正是和传递的参数一一对应,并且每个参数都添加了Autowired注解。这里我们要补充一点@Autowired注解的逻辑,上一篇我们在分析无参数页面跳转的时候,分析了@Route注解,被@Route注解的类会在编译期间生成辅助代码,那么@Autowired注解是否也有对应的辅助类生成呢?确实是有的,我们看一下生成的代码。
ini
public class KotlinTestActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
KotlinTestActivity substitute = (KotlinTestActivity)target;
substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
}
}
分析一下这个类生成的规则,大概可以知道,首先命名上是以类名+$$ARouter$$Autowired
的固定格式来命名的,每个要接收参数的页面都会生成一个这样的类。在KotlinTestActivity$$ARouter$$Autowired
中创建了一个SerializationService对象,它是一个继承了IProvider接口的接口,是ARouter内置的一个服务,当页面传递的数据为对象时,用来将对象和json进行相互转换,本次我们用不到这个类,所以暂不做关注。
我们再去看name和age的读取,就很直观了,直接拿到Activity对象获取intent,然后从intent上读到对应的值,再存入到Activity事先定义好的变量上。这就是@Autowired注解生成的辅助类所做的工作。所以看到这里我们也能猜到,ARouter.getInstance().inject(this)方法肯定是要找到这个辅助类。
再回到inject方法中,跟进到_ARouter类的inject方法,这里又创建了一个AutowiredService类型的对象,它也是一个继承了IProvider接口的接口,ARouter内置了一个实现类AutowiredServiceImpl。
scss
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
我们来到它的autowire方法。
typescript
@Override
public void autowire(Object instance) {
doInject(instance, null);
}
我们知道instance就是Activity对象,在这个方法中,会递归Activity的父类,直到父类为android的Activity为止。
scss
private void doInject(Object instance, Class<?> parent) {
Class<?> clazz = null == parent ? instance.getClass() : parent;
ISyringe syringe = getSyringe(clazz);
if (null != syringe) {
syringe.inject(instance);
}
Class<?> superClazz = clazz.getSuperclass();
// has parent and its not the class of framework.
if (null != superClazz && !superClazz.getName().startsWith("android")) {
doInject(instance, superClazz);
}
}
看下getSyringe方法,此方法会的到一个ISyringe类型的对象,然后调用了它的inject方法,我们发现KotlinTestActivity$$ARouter$$Autowired
也实现了ISyringe接口,这个inject方法和KotlinTestActivity$$ARouter$$Autowired
中的inject方法有可能是同一个,继续往下看getSyringe的实现。
ini
private ISyringe getSyringe(Class<?> clazz) {
String className = clazz.getName();
try {
if (!blackList.contains(className)) {
ISyringe syringeHelper = classCache.get(className);
if (null == syringeHelper) { // No cache.
syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
classCache.put(className, syringeHelper);
return syringeHelper;
}
} catch (Exception e) {
blackList.add(className); // This instance need not autowired.
}
return null;
}
我们看到这里通过反射创建了clazz.getName() + SUFFIX_AUTOWIRED
的对象并返回了,clazz.getName() + SUFFIX_AUTOWIRED
是什么?clazz是Activity的class,所以得到的name就是KotlinTestActivity,SUFFIX_AUTOWIRED是定义的常量,拼接起来之后正好是 <math xmlns="http://www.w3.org/1998/Math/MathML"> A R o u t e r ARouter </math>ARouterAutowired,所以如我们猜想的一样,这里得到的正是KotlinTestActivity$$ARouter$$Autowired
这个辅助类对象,这样一来,读取传递参数的逻辑就通了。
总结
我们来总结一下参数传递和接收的实现逻辑。首先,通过ARouter传递参数时,会将参数存入到Postcard上的Bundle对象中,最后通过intent跳转Activity的时候,将bundle中的数据交给intent,然后执行页面跳转;进入目标页面后,需要接收的参数都被@Autowired注解,代码编译期间,会根据这个注解生成辅助类类名+$$ARouter$$Autowired
,在目标页面调用inject方法找到这个辅助类并创建对象,执行其inject方法,即可在此通过intent将传递的参数取出,并赋值给页面定义的参数。如此一来,参数的传递和接收就完成了。