TheRouter 框架原理

TheRouter 框架入口方法

通过InnerTheRouterContentProvider 注册在AndroidManifest.xml中,在应用启动时初始化

    <application><providerandroid:name="com.therouter.InnerTheRouterContentProvider"android:authorities="${applicationId}.therouter.TheRouteContentProvider"android:exported="false" /></application>

入口方法为TheRouter.init(applicationContext)

    /*** TheRouter初始化方法。内部流程:<br>* 同步流程:<br>*     1. 首先初始化FlowTask的内置事件,BEFORE_THEROUTER_INITIALIZATION,以及依赖这个Task的全部任务。*         这个事件的目的是在TheRouter的路由初始化前做某些操作,例如修改路由表、添加路由拦截器等……*     2. 初始化跨模块依赖表*     3. 初始化路由表* 异步流程:<br>*     1. 调用FlowTask的外部事件*     2. 添加 @Autowired 路由解析器*/@JvmStaticfun init(context: Context?) {if (!inited) {......addFlowTask(context, digraph)......}}

在初始化前先调用addFlowTask(context, digraph)方法。

在跳转到对应方法实现处发现,该方法为空方法,没有具体业务逻辑。

@file:JvmName("TheRouterServiceProvideInjecter")package aimport android.content.Context
import com.therouter.flow.Digraph/*** Created by ZhangTao on 18/2/24.*/
fun trojan() {}
fun autowiredInject(obj: Any?) {}
fun addFlowTask(context: Context?, digraph: Digraph) {}
fun initDefaultRouteMap() {}

在学习了相关博客后,发现这里暗藏了TheRouter设计的巧妙之处,也正是因为这个特点,TheRouter框架在没有使用反射机制情况下,可以动态注入逻辑。

我们在按照TheRouter框架添加了各种注解后,在编译期间,会自动生成一个合成类,将我们添加了@FlowTask(taskName = "xxx")注解的方法全部放到一个静态public static void addFlowTask(android.content.Context context, com.therouter.flow.Digraph digraph)方法中。

Debug 时,编译器生成的对应合成文件在目录

编译时生成对应方法如下

	public static void addFlowTask(android.content.Context context, com.therouter.flow.Digraph digraph) {digraph.addTask(new com.therouter.flow.Task(false, "base_init", "", new com.therouter.flow.FlowTaskRunnable() {@Overridepublic void run() {com.ugreen.modulesbase.base.BaseInitTaskKt.initBase(context);}@Overridepublic String log() {return "com.ugreen.modulesbase.base.BaseInitTaskKt.initBase(context);";}}));digraph.addTask(new com.therouter.flow.Task(false, "init_device_info", "base_init", new com.therouter.flow.FlowTaskRunnable() {@Overridepublic void run() {com.ugreen.modulesbase.base.BaseInitTaskKt.initDeviceInfo(context);}@Overridepublic String log() {return "com.ugreen.modulesbase.base.BaseInitTaskKt.initDeviceInfo(context);";}}));digraph.addTask(new com.therouter.flow.Task(false, "base_init_language", "", new com.therouter.flow.FlowTaskRunnable() {@Overridepublic void run() {com.ugreen.modulesbase.base.BaseInitTaskKt.initLanguage(context);}@Overridepublic String log() {return "com.ugreen.modulesbase.base.BaseInitTaskKt.initLanguage(context);";}}));digraph.addTask(new com.therouter.flow.Task(false, "base_init_smartRefresh", "", new com.therouter.flow.FlowTaskRunnable() {@Overridepublic void run() {com.ugreen.modulesbase.base.BaseInitTaskKt.initSmartRefresh(context);}@Overridepublic String log() {return "com.ugreen.modulesbase.base.BaseInitTaskKt.initSmartRefresh(context);";}}));digraph.addTask(new com.therouter.flow.Task(false, "base_init_titleBar", "", new com.therouter.flow.FlowTaskRunnable() {@Overridepublic void run() {com.ugreen.modulesbase.base.BaseInitTaskKt.initTitleBar(context);}@Overridepublic String log() {return "com.ugreen.modulesbase.base.BaseInitTaskKt.initTitleBar(context);";}}));}

这个方法名是不是很熟悉,只看它的方法名和入参数,这和前面提到的空方法一模一样。

是的,你没有看错,这里编译期间生成的方法就是要通过字节码插桩技术替换空方法实现。

是不是很巧妙。

那么接着看TheRouter的init方法,开始执行digraph.beforeSchedule()方法

    /*** 由于initSchedule执行比较耗时需要放到异步,而Before需要在路由表初始化之前执行,需要同步* 所以单独列出一个方法,检测dependsOn只有beforTheRouterInit的任务,提前执行*/fun beforeSchedule() {val virtualFlowTask = getVirtualTask(TheRouterFlowTask.BEFORE_THEROUTER_INITIALIZATION)virtualTasks[TheRouterFlowTask.BEFORE_THEROUTER_INITIALIZATION] = virtualFlowTaskvirtualFlowTask.run()tasks.values.forEach {if (!it.async && it.dependencies.size == 1&& it.dependencies.contains(TheRouterFlowTask.BEFORE_THEROUTER_INITIALIZATION)) {// 此时一定在主线程,所以直接调用it.run()}}}

该方法同步执行,首先检测出dependsOn为beforTheRouterInit的任务,开始执行

查看源码发现,即使接入端未添加该dependsOn注解,这里也会默认创建一个virtualTask任务,这里不是很理解,后续补充。

beforeSchedule在执行完成virtualFlowTask后,开始执行依赖该task的任务,这里过滤条件要求,任务为同步执行且只依赖beforTheRouterInit的任务列表。

 接着看TheRouter的init方法,通过execute执行一个异步的代码片段

            execute {debug("init", "TheRouter.init() method do @FlowTask init")digraph.initSchedule()debug("init", "TheRouter.init() method do @FlowTask schedule")runInitFlowTask()}

initSchedule具体内容如下

    /*** 初始化方法*/fun initSchedule() {for (task in tasks.values) {fillTodoList(task)}inited = truependingTaskRunnableList.forEach {it.run()}}

这里重点方法如下

    private fun fillTodoList(root: Task) {if (!root.isDone()) {val dependsSet = getDepends(root)if (isNotEmpty(dependsSet)) {if (loopDependStack.contains(root)) {throw IllegalArgumentException("TheRouter::Digraph::Cyclic dependency " + getLog(loopDependStack,root))}loopDependStack.add(root)for (depend in dependsSet) {fillTodoList(depend)}loopDependStack.remove(root)if (!todoList.contains(root)) {todoList.add(root)}} else {if (!todoList.contains(root)) {todoList.add(root)}}}}

通过递归调用将task依赖任务转化成一个todoList队列里面,这样就能保证被依赖的task放在队列的前面,依赖的放置在队列后面。关于这个队列具体如何作用后续继续说明

继续回到execute 代码片段里,继续执行runInitFlowTask方法

/*** 当TheRouter初始化时,执行的FlowTask*/
fun runInitFlowTask() {TheRouter.runTask(TheRouterFlowTask.THEROUTER_INITIALIZATION)
}

接着继续查看Init方法,执行routerInject.asyncInitRouterInject(context)方法

该方法就是将路由相关的进行注入,例如配置的路由拦截器和自定义的拦截器

在asyncInitRouterInject方法中,判断mInterceptors为空时会从dex 文件中解析对应的拦截器,并实例化放入到mInterceptors对象里面,mInterceptors = TheRouterLinkedList<Interceptor>(16),最终也就是放入到该列表中,这里最多支持16个拦截器对象

    fun asyncInitRouterInject(context: Context?) {execute {trojan()if (mInterceptors.isEmpty()) {initServiceProvider(context)}}}

继续接着执行Init方法,开始执行asyncInitRouteMap()方法,该方法主要是用来加载路由配置

/*** 在异步初始化路由表*/
fun asyncInitRouteMap() {execute {debug("RouteMap", "will be add route map from: initDefaultRouteMap()")initDefaultRouteMap()initedRouteMap = trueif (initTask == null) {initRouteMap()} else {debug("RouteMap", "will be add route map from: RouterMapInitTask")initTask?.asyncInitRouteMap()}executeInMainThread {sendPendingNavigator()}}
}

这里为异步执行,首先执行一个initDefaultRouteMap的空方法,这里也应该是在编译期间通过字节码插桩替换,不过这里没有发现具体逻辑。

如何initTask为空,也就是前面空方法没有具体业务实现,那么这里就会从assets资源目录里面读取对应的路由配置,assets目录下var ROUTE_MAP_ASSETS_PATH = "therouter/routeMap.json" 在编译时会生成这样一个json文件

接着回来继续执行Init方法

            execute {context?.apply {(applicationContext as Application).registerActivityLifecycleCallbacks(TheRouterLifecycleCallback)}parserList.addFirst(DefaultObjectParser())parserList.addFirst(DefaultServiceParser())parserList.addFirst(DefaultUrlParser())parserList.addFirst(DefaultIdParser())}

这里注册全局的Activity生命周期监听,注册各种解析器,在编译期间根据添加@Autowired注解进行代码生成

这里使用不是很理解,后面补充。

以上就是TheRouter Init方法整体初始化流程。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI】机器学习——线性模型(线性回归)

线性模型既能体现出重要的基本思想&#xff0c;又能构造出功能更加强大的非线性模型 参考&#xff1a;唐宇迪机器学习课程 文章目录 3.1 线性模型3.1.1 数据3.1.2 目标/应用 3.2 线性回归3.2.1 回归模型历史3.2.2 回归分析研究内容回归分析步骤 3.2.3 回归分析分类3.2.4 回归模…

Flutter的oktoast插件详解

文章目录 简介详细介绍安装和导入导入在MaterialApp外面套一层OKToast组件为什么是包住MaterialApp&#xff1f; 显示Toast消息&#xff1a; 高级使用Toast位置Toast持续时间自定义Toast样式高级用法 使用场景提示消息表单验证操作反馈网络请求状态调试信息小结 总结 简介 okt…

数据治理中的核心元素——元数据

一、什么是元数据&#xff1f; 元数据是关于数据的组织&#xff0c;数据域及其关系的信息&#xff0c;简单来说&#xff0c;元数据就是被用来描述数据的信息。元数据是一个涵盖大量信息的数据集合。元数据可以为数据说明其元素或者属性&#xff08;名称&#xff0c;大小&#x…

029:vue项目,勾选后今天不再弹窗提示

第029个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

多元函数的微分法

目录 复合函数微分法 隐函数微分法 复合函数求导与全微分 隐函数偏导数与全微分 复合函数微分法 复合函数微分法是一种求导方法&#xff0c;用于计算复合函数的导数。 假设有一个复合函数yf(u)&#xff0c;其中ug(x)&#xff0c;则复合函数微分法可以用于计算y对x的导数。根…

使用SpringCloud Eureka 搭建EurekaServer 集群- 实现负载均衡故障容错【上】

&#x1f600;前言 本篇博文是关于使用SpringCloud Eureka 搭建EurekaServer 集群- 实现负载均衡&故障容错&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可…

Kafka3.0.0版本——消费者(独立消费者消费某一个主题数据案例__订阅主题)

目录 一、独立消费者消费某一个主题数据案例1.1、案例需求1.2、案例代码1.3、测试 一、独立消费者消费某一个主题数据案例 1.1、案例需求 创建一个独立消费者&#xff0c;消费firstTopic主题中数据&#xff0c;所下图所示&#xff1a; 注意&#xff1a;在消费者 API 代码中必…

JavaScript的基本数据类型如何使用?

JavaScript中的数据类型分为两大类&#xff0c;分别是基本数据类型和复杂数据类型(或称为引用数据类型)&#xff0c;如图所示。 本节重点讲解基本数据类型。下面我们用代码演示基本数据类型的使用。 (1)数字型(Number)&#xff0c;包含整型值和浮点型值: var numl 21; …

Unity中Shader使用最简屏幕坐标并且实现屏幕扭曲

文章目录 前言一、在之前写的shader中&#xff0c;用于对屏幕坐标取样的pos是在顶点着色器中完成计算的&#xff0c;然而还有一种更为简洁的方法&#xff0c;就是用顶点着色器中传给片元着色器的pos来给屏幕抓取进行采样原理&#xff1a;在顶点着色器中&#xff0c;o.pos是裁剪…

被删除并且被回收站清空的文件如何找回

文件的意外删除和回收站清空是许多用户面临的普遍问题。这种情况下&#xff0c;很多人会感到无助和焦虑&#xff0c;担心自己的重要文件永远丢失。然而&#xff0c;幸运的是&#xff0c;依然存在一些有效的方法能够帮助我们找回被删除并且被回收站清空的文件。 ▌被删除文件在…

计算机系统的基本概念

计算机系统的基本概念 本文主要以hello.c这个程序的整个生命周期来简单了解一下计算机系统结构的基本概念。 #include <stdio.h>int main() {printf("hello, world\n");return 0; }gcc hello.c -o hello ./hello hello, world此刻&#xff0c;hello.c源程序…

快速搭建超轻量级图床——Cpolar+和树洞外链

文章目录 1.前言2. 树洞外链网站搭建2.1. 树洞外链下载和安装2.2 树洞外链网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测试5.结语…

设计模式汇总

设计模式本质上是某类特定问题的代码设计解决方案&#xff0c;实际上是一套某类问题的代码设计经验总结。&#xff08;前辈总结的解决某类问题的切实可行的套路&#xff09; 问题 1、为什么要使用设计模式&#xff1f; 答&#xff1a;1、 提高代码复用率&#xff0c;降低开发成…

免费知识管理系统,让企业管理文档数据更便捷

编者按&#xff1a;本文详细介绍了免费强大的低代码平台在构建知识管理系统方面的优势&#xff0c;并介绍了其知识管理系统独特的功能。只需轻松操作&#xff0c;即可体验到该平台带来的便捷与高效&#xff01;快来了解如何利用这一神奇的工具&#xff0c;让知识管理变得更加轻…

内网穿透——Windows搭建服务器

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

第14章_瑞萨MCU零基础入门系列教程之QSPI

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

【golang】调度系列之m

调度系列 调度系列之goroutine 上一篇中介绍了goroutine&#xff0c;最本质的一句话就是goroutine是用户态的任务。我们通常说的goroutine运行其实严格来说并不准确&#xff0c;因为任务只能被执行。那么goroutine是被谁执行呢&#xff1f;是被m执行。 在GMP的架构中&#xff…

大橙子vfed 5.0去授权完美破解主题模版源码 | 苹果CMS

大橙子vfed 5.0去授权完美破解主题模版源码 | 苹果cms 大橙模版算是在苹果cms众多主题里&#xff0c;较为亮眼的一款了&#xff0c;主题简洁&#xff0c;功能众多&#xff0c;非常的齐全。 今天分享的就是大橙5.0版本模板&#xff0c;完美破解&#xff0c;自测无后门&#xf…

mysql中GROUP_CONCAT函数详解

GROUP_CONCAT是MySQL中的一个聚合函数&#xff0c;它用于将多行数据按照指定的顺序连接成一个字符串&#xff0c;并返回结果。下面是对GROUP_CONCAT函数的详解&#xff1a; 语法&#xff1a; GROUP_CONCAT([DISTINCT] expr [,expr …] [ORDER BY {unsigned_integer | col_name…

笔记(二)图的基本表示【斯坦福CS224W图机器学习】

1、基础知识 图是由节点和连接组成的 本体图&#xff0c;具体图是本体图的实例化&#xff0c;取决于想要解决什么问题 2、图的种类 异质图 异质图-二分图 异质图-二分图-展开 3、节点连接数 节点的度、入度和出度 4、图的基本表示 邻接矩阵 无向图的邻接矩阵是对称阵&#…