Evil Mouth's Blog

ARouter @Autowired 自动注入

May 31, 2020

前言

ARouter 有一个@Autowired 的注解,能自动帮我们赋值一些变量,例如

public class MainFragment {
    @Autowired
    String name;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Arouter.getInstance().inject(this);

        System.out.println(name);
    }
}

通过Arouter.getInstance().inject(this);就能将 Activity 传输的一些值自动帮我们赋值上对应变量,省去我们手动去调用getIntent().getString(xxx)

inject(this)

那么看下它做了什么,翻了几下发现,ARouter 会做以下几步操作 1、APT 编译期间扫描@Autowired 字段的文件并生成 MainFragment$$ARouter$$Autowired 文件

  • $$ARouter$$Autowired 类实现了 ISyringe 接口,拥有一个 inject(Object)的方法,这里面就是赋值的代码
public class MainFragment$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    MainFragment substitute = (MainFragment)target;
    substitute.name = substitute.getArguments().getString("name");
    substitute.age = substitute.getArguments().getInt("age");
  }
}

2、Arouter.getInstance().inject(this);则是通过反射创建了$$ARouter$$Autowired 类并调用 inject 方法实现自动赋值

@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();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }

思考

众所周知反射的性能是差的,那么有什么办法不反射吗,答案是有的,那就是直接 new MainFragment$$ARouter$$Autowired().inject(this);

然而在代码没编译之前,MainFragment$$ARouter$$Autowired 这个类是还没生成的,自然无法直接调用。况且这样还有个问题,那就是每个 Activity 或 Fragment 生成的类都是唯一的,我们也不可能在每个地方手动 new+inject,这样还不如反射来的方便

这时候就需要 Android 提供的工具 Transform 了

Transform 开发

核心原理

利用 Transform,在编译期间往使用了@Autowired 的 Activity 或 Fragment 类的 onCreate(Bundle)方法自动注入new xxx$$ARouter$$Autowired().inject(this);这行代码

过程

1、扫描整个项目里名称后缀为$$ARouter$$Autowired 的 class 文件 2、以此找到对应的 Activity 或 Fragment 文件 3、利用 ASM 库对该文件进行访问 4、访问到 onCreate(Bundle)方法时,在 super.onCreate 前写入 inject 方法

结果

编译完成后,可以通过 apk 包分析或这在 app\build\intermediates\transforms 目录下找到 MainFragment 文件编译后的代码,可以看见 MainFragment 的 onCreate 方法里面多了一行代码,就是上面所想要的 new MainFragment$$ARouter$$Autowired().inject(this);

再次思考

虽然利用 Transform 可以解决反射的问题,但无疑也带来了一个问题,就是项目协作上,其他人不了解的话会很奇怪。我的做法是在 $$ARouter$$Autowired 类加了行注释,起码别人在看这个类的时候能知道什么时候会 inject。 至于怎么加这行注释,就靠各位发挥了

本项目例子已开源Github

— Evil Mouth