Android开源框架系列-组件化之ARouter Activity页面参数传递源码分析

前言

在上一篇中我们分析了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将传递的参数取出,并赋值给页面定义的参数。如此一来,参数的传递和接收就完成了。

相关推荐
B.-1 小时前
Flutter 应用在真机上调试的流程
android·flutter·ios·xcode·android-studio
有趣的杰克1 小时前
Flutter【04】高性能表单架构设计
android·flutter·dart
放逐者-保持本心,方可放逐5 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
大耳猫7 小时前
主动测量View的宽高
android·ui
帅次9 小时前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
枯骨成佛10 小时前
Android中Crash Debug技巧
android
工业互联网专业14 小时前
Python毕业设计选题:基于Django+uniapp的公司订餐系统小程序
vue.js·python·小程序·django·uni-app·源码·课程设计
程序员小海绵【vincewm】15 小时前
【设计模式】结合Tomcat源码,分析外观模式/门面模式的特性和应用场景
设计模式·tomcat·源码·外观模式·1024程序员节·门面模式
kim565915 小时前
android studio 更改gradle版本方法(备忘)
android·ide·gradle·android studio
咸芝麻鱼15 小时前
Android Studio | 最新版本配置要求高,JDK运行环境不适配,导致无法启动App
android·ide·android studio