arouter-gradle-plugin version : 1.0.2
AutoRegister : https://github.com/luckybilly/AutoRegister
前言
在本系列的第一篇中讲过,ARouter 可以通过扫描 dex 文件中 class 的全类名,来加载 compiler 生成的路由类。但这种方式影响性能,并且效率也不高。所以在 ARouter v1.3.0 之后的版本中,加入了自动注册的方式进行路由表的加载,自动注册可以缩短初始化时间,解决应用加固导致无法直接访问 dex 文件从而初始化失败的问题。
那么自动注册到底是什么东东,为什么有这么强大的能力呢?
那么接下来,我们就来分析分析。
预先需要了解的知识点:
- 自定义 gradle plugin
- gradle transform api
- 使用 asm 实现字节码插桩
arouter-register
arouter-register 的入口就在 PluginLaunch
1 | public class PluginLaunch implements Plugin<Project> { |
从上面的代码可知:
- 只在 application module (一般都是 app module)生成自动注册的代码;
- 初始化了自动注册的设置,这样自动注册就知道需要注册 IRouteRoot IInterceptorGroup IProviderGroup 这三者;
- 注册 RegisterTransform ,字节码插桩将在 RegisterTransform 中完成;
可以看出,重点就在 RegisterTransform 里面。那我们重点就关注下 RegisterTransform 的代码,这里就贴出 transform 方法的源码了。(关于 Transform 的 InputTypes 和 Scopes 知识点在这就不讲了,如有需要了解的同学可以看 Android 热修复使用Gradle Plugin1.5改造Nuwa插件)
1 | class RegisterTransform extends Transform { |
上面代码的逻辑很清晰,按照之前设置好的 IRouteRoot IInterceptorGroup IProviderGroup 这三个接口,然后扫描整个项目的代码,分别找到这三者各自的实现类,然后加入到集合中。最后在 LogisticsCenter 中实现字节码插桩。
我们来详细看下 RegisterCodeGenerator.insertInitCodeTo(ext) 的代码
1 | static void insertInitCodeTo(ScanSetting registerSetting) { |
插入的操作在 insertInitCodeIntoJarFile 中实现。
1 | private File insertInitCodeIntoJarFile(File jarFile) { |
字节码插桩的代码还在 referHackWhenInit 方法中。
1 | //refer hack class when object init |
最终,生成的代码会像下面所示:
1 | private static void loadRouterMap() { |
那么顺便来跟踪一下 register 方法的代码,看看里面是如何完成路由表注册的。
1 | private static void register(String className) { |
这样相比之下,自动注册的方式确实比扫描 dex 文件更高效,扫描 dex 文件是在 app 运行时操作的,这样会影响 app 的性能,对用户造成不好的体验。而自动注册是在 build 的时候完成字节码插桩的,对运行时不产生影响。
学了今天这招,以后 compiler 生成的代码需要注册的步骤都可以通过自动注册来完成了,赞一个👍
番外
之前看到自动注册这么神奇,所以想看下插入字节码之后 LogisticsCenter 代码的效果,所以反编译了一下 ARouter demo apk,可以看到 LogisticsCenter.smali 的 loadRouterMap 方法:
1 | .method private static loadRouterMap()V |
确实符合我们的预期啊,真好!