给 Arouter 优化的一些小建议

简介: 给 Arouter 优化的一些小建议

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

1、混淆优化


image.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, "<init>", "()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 测了一下

目录
相关文章
|
6月前
|
人工智能 自然语言处理 安全
2026年智能客服系统哪家好?从技术到落地实战全面测评
2026年,智能客服已成为企业降本增效的核心工具。本文深入解析瓴羊Quick Service等五大领先系统,从全渠道整合、AI协同到数据安全,助力企业选型升级,实现服务智能化转型。
|
11月前
|
人工智能 物联网 API
ModelScope魔搭25年8月发布月报
🔥 这个夏天,开源热潮比气温更燃!Qwen3、GLM4.5、混元、Wan2.2、Qwen-Image等重磅模型密集发布,MoE、多模态、Agent、生图视频全爆发,ModelScope 全程 Day0 支持,生态持续进化中!
793 0
|
开发框架 Dart 前端开发
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
【4月更文挑战第30天】对比 Flutter(Dart,强类型,Google支持,快速热重载,高性能渲染)与 React Native(JavaScript,庞大生态,热重载,依赖原生渲染),文章讨论了开发语言、生态系统、性能、开发体验、学习曲线、社区支持及项目选择因素。两者各有优势,选择取决于项目需求、团队技能和长期维护考虑。参考文献包括官方文档和性能比较文章。
937 0
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
|
开发框架 JavaScript 前端开发
Electron 重大更新,33.0.0版本发布,带来多项新特性与改进!
本文介绍了 Electron 33.0.0 版本的重要更新,包括核心组件的升级(Chromium、Node.js 和 V8),新增功能(如 app.setClientCertRequestPasswordHandler 和 View.setBorderRadius),重要改进和主要问题修复。建议开发者尽快升级,以享受更强大的性能和功能。
984 0
Electron 重大更新,33.0.0版本发布,带来多项新特性与改进!
|
Java Maven 数据库
Annotation Processing Tool自动生成代码
本文介绍了一种利用Java注解处理器(Annotation Processor)自动生成协议接收与发送类接口的方法,显著提升开发效率。注解处理器能在编译阶段扫描并处理特定注解,生成所需Java代码。文中详细展示了如何通过自定义`HttpProto`注解及对应的处理器`ProtoServiceProcessor`,实现在保存协议类后自动生成客户端请求工具和服务端控制器代码。此外,还提供了具体实现步骤、依赖配置及常见问题解决方案,如处理“服务配置文件不正确”错误和Gradle项目的配置方法。此技术特别适用于需要频繁处理协议或数据交互的应用场景。
281 1
|
开发工具
用 Vim 编辑 Markdown 时直接粘贴图片
介绍一款 Vim 插件,可以帮助提升编辑 Markdown 时的从剪贴板粘贴图片的效率和体验。
390 2
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
523 8
|
开发框架 Dart 前端开发
Android 跨平台方案对比之Flutter 和 React Native
本文对比了 Flutter 和 React Native 这两个跨平台移动应用开发框架。Flutter 使用 Dart 语言,提供接近原生的性能和丰富的组件库;React Native 则基于 JavaScript,具备庞大的社区支持和灵活性。两者各有优势,选择时需考虑团队技能和项目需求。
1509 9
|
弹性计算 数据库 数据安全/隐私保护
阿里云服务器真香宝典之Calibre-Web个人图书馆云端部署
在阿里云ECS(2核2G,SSD40G,3M带宽)上,安装Ubuntu 22.04,然后配置Docker和FTP。创建 `/config` 和 `/books` 目录,设置权限,开放端口,拉取 `johngong/calibre-web` Docker镜像,以`calibre-web`命名容器,映射端口,配置环境变量,挂载卷,确保重启策略。本地安装Calibre客户端,上传metadata.db到服务器。在Calibre-web服务端配置数据库,启用上传权限,修改管理员账户信息。完成配置后,开始上传电子书并进行阅读。
2894 2
阿里云服务器真香宝典之Calibre-Web个人图书馆云端部署
|
存储 缓存 Java
Android性能优化:内存管理与LeakCanary技术详解
【7月更文挑战第21天】内存管理是Android性能优化的关键部分,而LeakCanary则是进行内存泄漏检测和修复的强大工具。