android router不起作用,给 Arouter 优化的一些小建议

Arouter 应该算是 Android 国民级框架了,在自己做组件化框架的时候,也是参考了不少 Arouter 的设计,在阅读源码中,觉得有的点是可以优化的,所以就有了今天的文章。

1、混淆优化

b4150ea38f2fc2ff7cddd00259fbfcf0.png

在 README 中可以看到,如果是开启混淆的话,需要添加如上的规则。主要原因是 gradle-plugin 在收集 apt 生成类的时候,注入到 LogisticsCenter 的是 className,然后运行时通过反射的方式来初始化类。

Arouter 代码

在 Arouter-gradle-plugin 模块的 RouteMethodVisitor 类中会对所有收集到的类,注入到 LogisticsCenter 的 loadRouterMap 方法中:

@Override

void visitInsn(int opcode) {

//generate code before return

if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {

extension.classList.each { name ->

name = name.replaceAll("/", ".")

mv.visitLdcInsn(name)//类名

// generate invoke register method into LogisticsCenter.loadRouterMap()

mv.visitMethodInsn(Opcodes.INVOKESTATIC

, ScanSetting.GENERATE_TO_CLASS_NAME

, ScanSetting.REGISTER_METHOD_NAME

, "(Ljava/lang/String;)V"

, false)

}

}

super.visitInsn(opcode)

}

复制代码

注入结果如下:

private static void loadRouterMap() {

registerByPlugin = false;

// 就大致举一个例子

register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");

...

}

复制代码

最后会通过 register 方法来完成反射初始化:

private static void register(String className) {

if (!TextUtils.isEmpty(className)) {

try {

Class> clazz = Class.forName(className);

Object obj = clazz.getConstructor().newInstance();

if (obj instanceof IRouteRoot) {

registerRouteRoot((IRouteRoot) obj);

}

...

}

}

复制代码

优化建议

将 String 类型的 className 替换成 Class

我们可以从 gradle-plugin 的 RouteMethodVisitor 入手,来更改一下 RouteMethodVisitor:

@Override

void visitInsn(int opcode) {

//generate code before return

if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {

extension.classList.each { name ->

// 获取 Class

mv.visitLdcInsn(Type.getType("L" + name + ";"))

// generate invoke register method into LogisticsCenter.loadRouterMap()

mv.visitMethodInsn(Opcodes.INVOKESTATIC

, ScanSetting.GENERATE_TO_CLASS_NAME

, ScanSetting.REGISTER_METHOD_NAME

, "(Ljava/lang/Class;)V"

, false)

}

}

super.visitInsn(opcode)

}

复制代码

注入的结果如下:

private static void loadRouterMap() {

registerByPlugin = false;

// 就大致举一个例子

register(com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava.class);

...

}

复制代码

还要再更改一下 register 方法,更改如下:

private static void register(Class clazz){

if (clazz!=null) {

try {

Object obj = clazz.getConstructor().newInstance();

if (obj instanceof IRouteRoot) {

registerRouteRoot((IRouteRoot) obj);

...

复制代码

2、反射优化

虽然混淆的问题可以通过 Class 来解决,但仍然无法解决反射带来的性能问题。我们在 ASM 插桩的时候是可以拿到类名的,那我们能不能通过 new 类()  的方式来初始化类呢?我们可以继续按照上面的思路来解决掉反射的问题。

继续来看 RouteMethodVisitor ,来更改一下 RouteMethodVisitor:

@Override

void visitInsn(int opcode) {

//generate code before return

if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {

extension.classList.each { name ->

mv.visitVarInsn(Opcodes.ALOAD, 0)

//用无参构造方法创建实例

mv.visitTypeInsn(Opcodes.NEW, name)

mv.visitInsn(Opcodes.DUP)

mv.visitMethodInsn(Opcodes.INVOKESPECIAL, name, "", "()V", false)

String interfaceName = ""

String insertMethod = ""

// ①、

if (name.contains("ARouter\$\$Root\$\$")) {

interfaceName = "com/alibaba/android/arouter/facade/template/IRouteRoot"

insertMethod = "registerRouteRoot"

} else if (name.contains("ARouter\$\$Interceptors\$\$")) {

interfaceName = "com/alibaba/android/arouter/facade/template/IInterceptorGroup"

insertMethod = "registerInterceptor"

} else if (name.contains("ARouter\$\$Providers\$\$")) {

interfaceName = "com/alibaba/android/arouter/facade/template/IProviderGroup"

insertMethod = "registerProvider"

}

if (!TextUtils.isEmpty(interfaceName) && !TextUtils.isEmpty(insertMethod)) {

mv.visitMethodInsn(Opcodes.INVOKESTATIC

, ScanSetting.GENERATE_TO_CLASS_NAME

, insertMethod

, "(L${interfaceName};)V"

, false)

}

}

}

super.visitInsn(opcode)

}

复制代码

①:根据类名来判断当前是 Root、Interceptors 还是 Providers,因为 Arouter 在 apt 生成类的时候会对有一个类命名规则,我们只要根据这个规则,即可找到该类实现的是哪个接口。这个地方还需要有一个 insertMethod,因为我们是 new 类() 的方式,不能像 register 方法那样通过 Class 来做统一处理,我们需要明确的类型来注入,所以,这个地方用的是接口。

注入效果如下:

private static void loadRouterMap() {

registerByPlugin = false;

registerRouteRoot(new com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava());

registerInterceptor(new com.alibaba.android.arouter.routes.ARouter$$Interceptors$$app());

registerProvider(new com.alibaba.android.arouter.routes.ARouter$$Providers$$app());

...

}

复制代码

3、一些有趣的 issue

1、issue 776 : kt 中注入 Autowired 无效

Arouter Autowired注入的时候在存在 kotlin-java 兼容性问题

针对基本数据类型的传递

var showBadge: Boolean? = null 注入失败

var showBadge: Boolean? = false 注入成功

原因:

在 arouter 生成代码中,获取注入变量通过 substitute.showBadge = substitute.getIntent().getBooleanExtra("showBadge", substitute.showBadge) ,对于基本数据类型的获取接口都带有默认参数,如 getBooleanExtra(String name, boolean defaultValue), 问题出在默认参数 对应 java boolean 类型没有 null 的概念 ,报错 can't unbox a null value

2、issue 818 : androidx 项目中使用 Arouter 的困惑

这个是我自己提的 🤣 ,主要是在看 arouter-compiler 源码的时候,Arouter 写死的是 support.fragment 的路径,为什么 types.isSubtype 的判断对于 androidx.fragment 却可以通过的问题,Jetifier 其实是不会将常量的 support 替换成 androidx 的,因为我写了一个 support 转 androidx 的 demo 测了一下

... 待补充吧

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[给 Arouter 优化的一些小建议]http://www.zyiz.net/tech/detail-143213.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值