TheRouter 框架原理

news/2024/10/18 0:20:20/

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.ppmy.cn/news/1095846.html

相关文章

决策单调性优化dp

区间类: P1880 [NOI1995] 石子合并 f i , j m a x ( f i , k f k 1 , j ) w i , j f_{i,j}max(f_{i,k}f_{k1,j})w_{i,j} fi,j​max(fi,k​fk1,j​)wi,j​ 若 w i , j w_{i,j} wi,j​满足区间单调性和四边形不等式&#xff0c;则 f i , j f_{i,j} fi,j​满足四边形不等式 …

springboot 请求https的私有证书验证

一、方案描述 我这里采用RestTemplate的方式调用https请求&#xff0c;请求第三方接口获取数据&#xff0c;证书由第三方私自签发的证书&#xff0c;我们构建的是一个springboot的API项目。 1.pom文件引入jar <dependencies><dependency><groupId>org.spr…

OmniGraffle Pro for Mac 中文正式版(附注册码) 苹果电脑 思维导图软件

OmniGraffle Pro是OmniGraffle的高级版本&#xff0c;它提供了更多的功能和工具&#xff0c;可以帮助用户创建更为复杂和高级的图表和流程图。OmniGraffle Pro支持自定义形状、图形、线条和箭头等&#xff0c;可以让用户创建出更加精细的图表。此外&#xff0c;OmniGraffle Pro…

《热题100》字符串、双指针、贪心算法篇

思路&#xff1a;对于输入的的字符串&#xff0c;只有三种可能&#xff0c;ipv4,ipv6,和neither ipv4:四位&#xff0c;十进制&#xff0c;无前导0&#xff0c;小于256 ipv6:八位&#xff0c;十六进制&#xff0c;无多余0&#xff08;00情况不允许&#xff09;&#xff0c;不…

Kubernetes禁止调度

在Kubernetes中&#xff0c;您可以通过几种方式来禁止某个Pod调度到节点上。以下是一些方法&#xff1a; Node Selector&#xff1a;您可以使用Node Selector来限制Pod只能调度到带有特定标签的节点上。如果您希望完全禁止Pod调度到某些节点上&#xff0c;可以确保这些节点不拥…

Vim 插件应用篇 vim-plug:简洁高效的Vim插件管理工具

用插件管理插件 Vim-plug介绍 Vim-plug 是一个Vim插件管理器&#xff0c;利用异步并行可以快速地安装、更新和卸载插件。它的安装和配置都非常简单&#xff0c;而且在操作过程中会给出很多易读的反馈信息&#xff0c;是一个自由、开源、速度非常快的、并行地安装或更新插件&a…

css flex:1;详解,配合demo效果解答

前言 给设置了display&#xff1a;flex的子组件设置了flex&#xff1a;1&#xff1b;就能让他填满整个容器&#xff0c;如果有多个就平均 flex&#xff1a;1&#xff1b;是另外三个样式属性的简写&#xff0c;等同 flex-grow: 0; flex-shrink: 1; flex-basis: auto;我们就针…

【数据仓库基础(三)】抽取-转换-装载

文章目录 一. ETL概念二. 数据抽取1&#xff0e;逻辑抽取2&#xff0e;物理抽取3&#xff0e;变化数据捕获 三. 数据转换四. 数据装载 一. ETL概念 ETL一词&#xff0c;它是Extract、Transform、Load三个英文单词首字母的简写&#xff0c;中文意为抽取、转换、装载。ETL是建立…