页面管理器是一个用于触发和管理app内页面跳转展示的模块,通过该模块调用方可以很方便的触发页面的跳转并传递自定义的参数。将页面的切换操作抽象为一种URI触发机制,克服了不同页面对应不同VC(viewController)时跳转页面时的不灵活性。下面我们将页面跳转管理器统称为PM(Page Manager)
为什么PM?
在我们平时的应用开发过程中,我们应该都碰到过根据远程数据配置页面入口的情况,通常我们会枚举所有情况,根据不同的入参决定要切换到的页面和切换页面的形态,这样做一般会存在如下问题:
- 页面切换逻辑太过分散,不便管理维护
- 不同的触发位置都需要实现一份分发逻辑,造成大量的冗余代码
- 分发方法过于庞大,极大的影响可读性
- 运营位(数据)、动态入口配置不灵活
而PM的提出就是为了解决以前分散的页面跳转带来的不便,而将页面跳转统一成为一种URI的形式来做处理,这样实现的优势基本上归结为以下几点:
- 页面跳转(VC切换)抽象为一种URI,方便统一分发管理
- 可以避免在非主线程触发页面切换时引起的crash
- 统一的URI定位符处理,同时处理web页面和native页面的天然切换
- 对传入参数进行字符话处理,可以通过简单的URI初始化对象,而不需要关心具体的参数初始化,类似HTTP请求
- 使底层页面切换方法针对上层透明,接入方不需要关心是否是平台自身实现的容器VC
- 方便监控页面切换指数(性能等)
怎么实现?
这里我们可以思考下,当为了满足上述优势的情况下,该怎么实现?
首先,我们需要一个规则来映射到我们app中对应的VC;我们还需要一个业务模块来屏蔽app内页面的切换逻辑;还需要一个将映射规则解析成PM能懂的解析器;还需要一个调度页面跳转的核心模块,….
总结出来分为如下几个模块:
- 文件管理模块–>负责处理映射规则文件
- URI解析模块–>负责解析URI,将URI以我们定义的规则拆分,并提供获取相关信息的接口
- 数据工厂–>根据文件管理模块、URI解析模块输出产生PM能处理的数据
- 解析引擎–>负责调度处理文件管理模块、URI解析模块、数据工厂,获取合适的路径
- 业务模块–>负责告知PM,本地页面切换方式,用于屏蔽不同APP之间的差异
- 核心模块–>负责处理页面切换的核心逻辑
文件管理模块–FileAdapter
FileAdapter作为一个文件适配器,用来处理文件的获取(网络、bundle、缓存),并将文件输出为PM可识别的类型。并对外提供如下接口
我们也可以在FileAdapter内做一些文件的获取逻辑和读取逻辑。
例如,在demo中,我们支持的文件类型是macox和iOS中最常见的plist类型,所以我们需要在FileAdapter中对plist文件进行解析,并转换成PageManager可识别的NSDictionary类型。如果我们的原文件是Json格式的话,我们只需要重新继承实现FileAdapter,对新有的逻辑进行处理即可
对FileAdapter的理解可以为一个抽象的接口层,我们默认实现了一种Plist格式文件支持。如果调用方需要自定义文件的获取方式,可以继承该类实现自己的FileAdapter
URI解析模块–URIAdapter
URIAdapter模块的主要工作就是根据我们的URI解析出我们需要的信息,通过URIAdapter,可以在URI提取出URI对应的host、pathComponent、parameters等信息
同FileAdapter一样,URIAdapter也可以理解为一个抽象的接口层,提供一个默认的URI解析规则,如果使用者需要适配以往的URI或者定义自身的URI拼接规则,指需要继承URIAdapter来实现自己的URIAdapter即可
URIAdapter为外部提供了如下接口:
这里需要对默认的URI拼接方式做一个说明,默认的URI结构如下
符合标准的URI定义格式,我们根据xxxx:(scheme)来区分应用,用page.xx(host)来区分模块,其他相应的模块来控制页面切换时的形态或者初始化参数
数据工厂–DataEngine
顾名思义,DataEngine是用于生产数据的模块,它根据FileAdapter和URIAdapter数据来生产页面跳转必须的业务数据,该模块主要负责产生页面跳转时必须的数据(参数、VC),并返回跳转路径
fixedPath是一个路径序列,每个item为一个ManagerPathNode对象
数据工厂,还担负了校验参数的任务,负责校验必传参数是否传递了和参数格式是否正常等。如果不满足需求则数据工厂返回的fixedPath则为空
解析引擎–PageManagerParserRuleEngine
解析引擎,很明显,作为一个引擎,负责联动各个模块完成由URI–>Data的任务。所以对于解析引擎来说,需要知道协同的模块都有哪些,这些协同模块是怎么工作的。解析引擎需要实现一个接口,使用者可根据自己的需求自定义自己的解析引擎。也可以将解析引擎单独拆分出来应用于其他模块
业务模块
这里的页面模块指的是根具体使用方比较相关的部分。为了完成PM的可移植性,在初期对对具体平台相关的页面切换方法进行了包装,针对不同的业务场景我们只需要改变
这里我们对普通的VC做了如下抽象
- 针对NSObject对象实现了VC的接口,方便在自定义VC的应用中快速的移植
- 人为的VC区分为普通VC和容器VC(ContainerIViewController)
- 对VC实现页面切换操作接口
这样做了以后,PM中对页面的调整和APP中页面的原生跳转逻辑成为了完全分离的。不依赖于任何系统原生的VC,可以屏蔽不同APP中的差异。当然在引入灵活性的同时也加大了嵌入PM的成本,我们需要为自己实现的容器VC实现响应的接口,来保证PM的功能完整性
核心模块–CCPageManager
该模块是整个PM的核心模块,或者说是中枢模块,用来驱动调配各个模块工作完成页面的跳转工作。为了让该模块正常工作,我们需要知道页面栈当前的状态和位置。所以需要提供一个初始化方法来获取rootViewController,而且提供一个出发页面跳转的方法。具体对外暴露了如下接口
辅助模块–util
该模块主要提供一些工具类,例如如何用序列化数据初始化一个对象等等。在PM中,最关键的初始化方法即属于该模块。关于实现方式在前面的文章中已经做了介绍,这里就不赘述
流程图
如何使用PM
在使用过程中,我们分为几种情况
引入PageManager到工程
1.如果我们的工程是以cocoaPods管理的,只需要在Podfile中加入
2.如果我们是自行管理第三方代码的话,则需要手动将PageManager的framework引入工程
完全使用PM的各个模块个解析规则
- 注册PM到应用
2 . 实现自己的FileAdatper,注册对应的映射文件
3 . 构建自己的映射文件,如下形式
4.发送URI给PageManager
改变规则后的调用
- 如果改变了URI解析规则,请重写URIAdapter中的方法,使之正确解析
- 如果改变了映射文件的格式,请重写FileAdapter中的方法
- 如果数据格式改变,请重写DataEngine中的方法
这里的重写都指的是继承覆盖
附录
1.Demo中配置文件的格式说明
key->页面标示符
class->对应页面viewController名称
attributes->参数列表,item为一个包含name和value的map
attributes[item]->name 表示参数的名称,可与viewcontroller中的property、instanceVariable对应
attributes[item]->value 表示与name对应的变量应初始化成的value,如果以URI的形式传递参数的话,只支持基础格式
attributes[item]->default 与name对应的变量如果为空时的默认值
attributes[item]->required 是否必传