Android-三方框架的源码

ARouter

Arouter的整体思路是moduelA通过中间人ARouter把路由信息的存到仓库WareHouse;moduleB发起路由时,再通过中间人ARouter从仓库WareHouse取出路由信息,这要就实现了没有依赖的两者之间的跳转与通信。其中涉及Activity的跳转、服务provider的获取、拦截器的处理等。

路由元信息是怎么收集的?
跳转Activity最终必定是走到了 startActivity(intent)方法,而intent是一般需要目标Activity的Class,构建PostCard,继承自路由元信息 RouteMeta,参数group是path的第一级,group的作用是作为路由的默认分组。Warehouse意为仓库,用于存放被 @Route、@Interceptor注释的 路由相关的信息,也就是我们关注的destination等信息。由于进行activity跳转需要目标Activity的class对象来构建intent,所以必须有一个中间人,把路径"/test/activity"翻译成Activity的class对象,然后moduleB才能实现跳转。 Warehouse,它存着所有路由信息。
Warehouse存了哪些信息呢?

class Warehouse {
    //所有IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级
    //(IRouteGroup实现类是编译时生成,代表一个组,即path第一级相同的所有路由,包括Activity和Provider服务)
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); 
    //所有路由元信息,是在completion中赋值,key是path
    //首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务
    static Map<String, RouteMeta> routes = new HashMap<>();
    
    //所有服务provider实例,在completion中赋值,key是IProvider实现类的class
    static Map<Class, IProvider> providers = new HashMap<>();
    //所有provider服务的元信息(实现类的class对象),是在ARouter初始化中赋值,key是IProvider实现类的全类名。
    //主要用于使用IProvider实现类的class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
    static Map<String, RouteMeta> providersIndex = new HashMap<>();
    
    //所有拦截器实现类的class对象,是在ARouter初始化时收集到,key是优先级
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");
    //所有拦截器实例,是在ARouter初始化完成后立即创建
    static List<IInterceptor> interceptors = new ArrayList<>();
...
}

groupsIndex,所有路由组元信息。是所有IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级。IRouteGroup实现类是编译时生成,代表一个组,即path第一级相同的所有路由,包括Activity和Provider服务)。
routes,所有路由元信息。是在LogisticsCenter.completion中赋值,key是path。首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务
providers,所有服务provider实例。在LogisticsCenter.completion中赋值,key是IProvider实现类的class
providersIndex,所有provider服务元信息(实现类的class对象)。是在ARouter初始化中赋值,key是IProvider实现类的全类名。用于使用IProvider实现类class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
interceptorsIndex,所有拦截器实现类class对象。是在ARouter初始化时收集到,key是优先级
interceptors,所有拦截器实例。是在ARouter初始化完成后立即创建
其中groupsIndex、providersIndex、interceptorsIndex是ARouter初始化时就准备好的基础信息,为业务中随时发起路由操作(Activity跳转、服务获取、拦截器处理)做好准备。

ARouter 框架的设计是它默认会将注解中注册path路径中第一个路由层级 (例如 "/trade/homePage"中的trade)作为该路由信息所的Group, 相同Group路径的路由信息会合并到最终生成的同一个类 的注册函数中进行同步注册。在大型项目中,对于复杂业务线同一个Group下可能包含上百个注册信息,注册逻辑执行过程耗时较长。

ARouter在编译时生成的帮助类PluginLaunch、RegisterTransform、ScanUtil这三个类,是用于对所有使用@Route、@Interceptor注解的类信息的分组和收集,编译运行时对路由信息仓库Warehouse的填充和使用。这里涉及到的是Annotation Process Tool(APT)技术,即注解处理工具。 除了运行时查找dex,还可以在编译时扫描帮助类信息,并且直接在物流中心LogisticsCenter loadRouterMap()方法中直接插入使用帮助类的代码,这里涉及 Android Gradle Plugin(AGP)技术,即Android的gradle插件相关技术。Android官方的AGP中提供了一个API——Transform,在class文件转为dex文件之前,这个节点可以拿到参与构建的所有class文件,在Transform的中拿到所有class文件后,通过ASM字节码操作框架,使用ASM对源码生产class文件和三方jar中的class文件进行扫描,找到所有帮助类,也就是PluginLaunch、RegisterTransform、ScanUtil这三个类,PluginLaunch,就是自定义的Gradle插件,然后注册了自定义的RegisterTransform。RegisterTransform中的registerList保存着的是各个帮助类的接口。RegisterTransform是自定义的Transform,transform方法会在gradle的执行阶段执行——源码生成class文件之后、class被打进dex文件之前。具体逻辑就是 扫描全部jar(三方库中的class文件)、扫描全部目录(源码生成的class文件),拿到所有目标帮助类,在 LogisticsCenter#loadRouterMap()中插入代码。

Arouter怎么找到这个路由的?
具体的扫描过程是使用ScanUtil类,插入代码是RegisterCodeGenerator类。首先是拿到jar中LogisticsCenter.class文件的输入流,接着使用ASM自定义MyClassVisitor在visitMethod()方法中找到要插入代码的方法-loadRouterMap()。然后在自定义RouteMethodVisitor的visitInsn()方法中,确保在return之前插入代码。遍历所有帮助类,把帮助类路径中的"/“换成”." 。 LogisticsCenter 在loadRouterMap方法主动注册插件 会在此处插入代码。调用此方法就注册了全部的 Routers、Interceptors、Provider。

如何使用编译时生成的帮助类呢?
https://juejin.cn/post/7201879428086513720#heading-0
Gradle开发,APT采集页面路由信息:https://blog.csdn.net/weixin_46039528/article/details/132930598
https://blog.csdn.net/weixin_46039528/article/details/133105627
Gradle开发(三),字节码插桩,编译期间自动注册收集页面路由信息的映射表类并汇总:
https://blog.csdn.net/weixin_46039528/article/details/133366126
Android编译优化系列-kapt篇:https://juejin.cn/post/7070849501166059551

APT(Annotation Processing Tool),即 注解处理器,是javac中提供的编译时扫描和处理注解的工具,它对源代码文件进行检测找出其中的注解,然后使用注解进行额外的处理。
注解就像是一个标签,有很多类型,可以贴在某些元素上面进行标记,并且标签上可以写一些信息。APT就是用来处理标签的工具,在编译开始后,可以拿到自己所关心的类型的所有标签,然后根据标签信息和被标记的元素信息,做一些事情。做那些事呢,这就看你如何写APT了,你让他干啥他就干啥,通常都是会生成一些帮助类——帮助完成你的目的的类。 后面无论对这种标签的使用是增加、减少了,每次编译都会重新走这一过程,而上一次的处理结果会被清空。
宏观上理解,APT就是javac提供给开发者在编译时处理注解的一种技术;微观上,具体到实例中就是指 继承自javax.annotation.processing.AbstractProcessor 的实现类,即一个处理特定注解的处理器。(下文提到的APT都是宏观上理解,具体的处理器简称为Processor)
使用了APT,只要使用注解进行标记即可,无论使用者怎么标记,每次编译时都由APT统一处理,不会出错、也不担心有遗漏。
APT还有两个特点:
1、获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能。
2、注意APT并不能对源文件进行修改,只能获取注解信息和被注解对象的信息,然后做一些自定义的处理,例如生成java类。
在这里插入图片描述
在Java源码到class文件之间,需要经过注解处理器的处理,注解处理器生成的代码也同样会经过这一过程,最终一起生成class文件。在Android中,class文件还会被打进Dex文件中,最后生成APK文件。
在编译流程进入Processor前,APT会对整个Java源文件进行扫描,这样就会获取到 所有添加了的注解和对应被注解的类。 注解和被注解的类,一起被视为一个元素,即TypeElement,就是process()方法参数annotations的数据类型。 通过TypeElement,我们可以获取注解的所有信息、被注解类的所有信息,这样就可以根据这些信息来生成 我们需要的帮助类了。
最重要的就是 process()方法的实现:拿到所有关注的注解元素后,就是每个Processor的独自的逻辑——解析注解并生成需要的java文件。

Java文件要如何生成呢?这里就要介绍 javepoet 这个库了,通常APT框架中生成Java类都是使用javepoet。这里也不展开介绍,下面结合我添加的注释即可理解。

Gradle:一个自动化构建框架;在Android开发中,用来编译打包输出apk,还可以用来添加三方依赖。
Apk打包流程包括很多环节:源码文件经过JavaCompiler转为class文件、class经过dex操作变为dex文件、dex和资源等打包成apk、签名 等等一系列步骤,这就需要Google使用Gradle把这些操作都串联起来。

https://juejin.cn/post/7129157665732689934

LeakCanary

为弱引用指定一个引用队列,当弱引用指向的对象被回收时,此弱引用就会被添加到这个队列中,我们可以通过判断这个队列中有没有这个弱引用,来判断该弱引用指向的对象是否被回收了。
精简流程如下所示:
1、LeakCanary.install(application);此时使用application进行
2、registerActivityLifecycleCallbacks,从而来监听Activity的何时被destroy。
3、在onActivityDestroyed(Activity activity)的回调中,去检测Activity是否被回收,检测方式如以下步骤:
使用一个弱引用WeakReference指向这个activity,并且给这个弱引用指定一个引用队列queue,同时创建一个key来标识该activity。
然后将检测的方法ensureGone()投递到空闲消息队列。
当空闲消息执行的时候,去检测queue里面是否存在刚刚的弱引用,如果存在,则说明此activity已经被回收,就移除对应的key,没有内存泄漏发生。
如果queue里不存在刚刚的弱引用,则手动进行一次gc。
gc之后再次检测queue里面是否存在刚刚的弱引用,如果不存在,则说明此activity还没有被回收,此时已经发生了内存泄漏,直接dump堆栈信息并打印日志,否则没有发生内存泄漏,流程结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/334570.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024 Move开发者大会在上海圆满落幕,第三期星航计划启动

*以下文章来源于MoveFuns &#xff0c;作者MoveFuns 至各位Mover&#xff1a; 2024 Move DevConfShanghai 圆满成功。在这次盛会上&#xff0c;我们围绕着Move生态关键的一年这一主题进行了深入的讨论和交流。 以下是本次会议的总结回顾&#xff1a; 大会盛况 本次大会在上…

大模型学习与实践笔记(九)

一、LMDeply方式部署 使用 LMDeploy 以本地对话方式部署 InternLM-Chat-7B 模型&#xff0c;生成 300 字的小故事 2.api 方式部署 运行 结果&#xff1a; 显存占用&#xff1a; 二、报错与解决方案 在使用命令&#xff0c;对lmdeploy 进行源码安装是时&#xff0c;报错 1.源…

innoDB存储引擎

1.逻辑存储结构 行数据->行->页->区->段->表空间 表空间(ibd文件)&#xff0c;一个mysql实例可以对应多个表空间&#xff0c;来存储记录&#xff0c;索引等数据。 段&#xff1a;分为数据段和索引段&#xff0c;回滚段&#xff0c;数据段就是B树的叶子节点&am…

神器yakit之web fuzzer功能

前言 yakit并不像burp一样单独设置爆破模块&#xff0c;但是yakit也是可以爆破的&#xff0c;并且更好用&#xff08;个人感觉&#xff09;。 手工测试场景中需要渗透人员对报文进行反复的发送畸形或者特定的payload进行查看服务器的反馈并以此来进行下一步的判断。 Fuzz标签便…

Gazebo的模型下载。

git clone zouxu634866/gazebo_modelshttps://gitee.com/zouxu6348660/gazebo_models.git&#xff0c;并完成路径配置。 &#xff08;本文提供了gitee下载&#xff0c;国外的Github下载较慢。&#xff09;

《WebKit 技术内幕》之四(1): 资源加载和网络栈

第四章 资源加载和网络栈 使用网络栈来下载网页和网页资源是渲染引擎工作的第一步 1.WebKit 资源加载机制 1.1 资源 网页本身就是一种资源、网页还需要依赖很多其他的资源(图片、视频) &#xff08;1&#xff09;HTML 支持的资源主要包括以下几种类型&#xff1a; HTML 页…

【并发编程系列】putIfAbsent和getOrDefault用法

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

高效火情监测,科技助力森林防火【数字地球开放平台】

数字地球开放平台-以卫星遥感为核心的空天信息服务开放平台 (geovisearth.com) 2019年3月30日&#xff0c;四川省凉山州木里县爆发了一场森林火灾&#xff0c;火点位于海拔3800米左右&#xff0c;地形险峻、坡度陡峭、谷深难以抵挡火势。在扑救的过程中&#xff0c;27名森林消防…

服务器自动拉取git代码运行脚本

# 1.场景分析 工作中常常会遇到本地编辑shell脚本或者python脚本完成后需要在服务器上运行的情况&#xff0c;每次进行拷贝费时费力。下面介绍下通过git管理器&#xff0c;实现本地与服务器代码同步的方式。选择公司搭建的gitlab为例&#xff1a; 2.gitlab配置服务器ssh密钥 …

【陈老板赠书活动 - 22期】- 人工智能(第三版)

陈老老老板&#x1f9d9;‍♂️ &#x1f46e;‍♂️本文专栏&#xff1a;赠书活动专栏&#xff08;为大家争取的福利&#xff0c;免费送书&#xff09; &#x1f934;本文简述&#xff1a;活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f473;‍♂️上一篇文章&#xff…

shopee西班牙站选品:如何在Shopee西班牙站点上进行选品策略

在Shopee西班牙站点进行选品时&#xff0c;卖家需要考虑一系列策略&#xff0c;以确保产品符合当地市场需求并取得良好的销售业绩。以下是一些关键策略&#xff0c;卖家可以参考并应用到自己的选品过程中。 先给大家推荐一款shopee知虾数据运营工具知虾免费体验地址&#xff0…

数据中心设备与线缆数据采集神器

​前言 在数据中心机房运维管理过程中&#xff0c;开展以下工作时通常我们采用手工表格的方式&#xff0c;效率低还容易出错&#xff0c;如&#xff1a; 数据中心机柜设备与线缆规划&#xff1b; 数据中心机柜设备与线缆变更记录&#xff1b; 数据中心机柜内设备与线缆数据采…

ACL【新华三与华为的区别】

【解释】acl简单点解释就是&#xff0c;一套根据需求而设置的规则 【背景】 192.168.1.0/24 网段不允许访问 192.168.2.0/24 网段&#xff0c;要求使用基本 ACL 实现20_1 可以访问 20_6 的 TELNET 服务&#xff0c;但不能访问 FTP 服务 【操作步骤】 {易混点 }&#xff1a;1. …

小扎宣布进军AGI!Meta正在训练Llama 3,目标是60万块H100!模型仍会开源

今日&#xff0c;扎克伯格在Instagram上通过一则视频官宣&#xff0c;Meta要进军AGI了&#xff01; 扎克伯格认定Meta的长期愿景是构建通用智能&#xff0c;将其负责任地开源&#xff0c;使其广泛可用并造福全人类&#xff0c;扎克伯格还透露了以下几点信息&#xff1a; 1、将…

探索设计模式的魅力:“感受单例模式的力量与神秘” - 掌握编程的王牌技巧

在软件开发的赛场上&#xff0c;单例模式以其独特的魅力长期占据着重要的地位。作为设计模式中的一员&#xff0c;它在整个软件工程的棋盘上扮演着关键性角色。本文将带你深入探索单例模式的神秘面纱&#xff0c;从历史渊源到现代应用&#xff0c;从基础实现到高级技巧&#xf…

2024Java零基础自学路线(Java基础、Java高并发、MySQL、Spring、Redis、设计模式、Spring Cloud)

目录 一、Java基础1、Java 基础3、Java8新特性4、Java集合5、Java高并发6、Java代码实例 二、MySQL数据库三、Spring Boot框架&#xff08;35天&#xff09;四、微服务Spring Cloud四、Redis中间件五、MongoDB数据库六、Netty网络编程七、23种设计模式八、Dubbo九、JavaScript零…

【stm32】hal库学习笔记-GPIO按键控制LED和蜂鸣器(超详细!)

【stm32】hal库学习笔记-GPIO按键控制LED和蜂鸣器 注&#xff1a;本学习笔记基于stm32f4系列 使用的开发板为正点原子stmf407ZGT6探索者开发板 GPIO引脚使用时&#xff0c;可输入或输出数字信号 例如: 检测按键输入信号&#xff08;Read_Pin&#xff09;输出信号&#xff08;W…

Git学习笔记(第2章):Git安装

官网地址&#xff1a;Githttps://git-scm.com/ Step1&#xff1a;查看Git的GNU协议 → 点击“Next” Step2&#xff1a;设置Git的安装位置(非中文、无空格的目录) → 点击“Next” Step3&#xff1a;选择Git的选项配置(推荐默认设置) → 点击“Next” Step4&#xff1a;设置Git…

MySQL复合查询解析

&#x1f388;行百里者半九十&#x1f388; &#x1f388;目录&#x1f388; 概念多表查询自连接子查询单行子查询多行子查询in关键字all关键字any关键字 多列子查询在from中使用子查询合并查询unionunion all 总结 概念 之前我们很多的查询都只是对于单表进行查询&#xff0c…