前言
开发客户端路由,一是为了管理客户端内部的模块通信,二是为客户端外部提供与客户端通信的渠道。
内部通信的使用目的是解耦模块之间的依赖,避免引入模块导致的多余维护工作,也可以解决在可能出现循环依赖的模块之间通信问题。同时,引入路由方案,能更好的维护与管理模块的API。
外部通信的使用目的是为产品打造通用的客户端通信协议,比如从推送跳转到用户聊天界面,从浏览器的网页唤起客户端,打开指定内容页面,为小程序的H5端提供Native能力。外部协议更可以统一整个平台客户端的通信API,统一不同平台客户端(如Win、Mac、Android、iOS)的能力。
架构设计
反射跳转管理模块(Performer)
借鉴CTMediator的实现思路,利用OC的Runtime机制,调用函数动态生成来解决模块依赖的问题。
通过TargetClassName(String)、SeletorName(String)、Params(Map)、UserInfo(Map)、Callback(Block)来发起一次函数调用,异步调用通过Callback实现。
在调用前,需要先对函数的各个参数进行基本校验。校验都通过后,使用NSInvocation来组装函数的调用。
需要注意的是,接收NSInvocation的返回值时,返回值的引用计数是需要手动管理的。
路由规则模型(RuleModel)
参考传统的URL规则,抽象出路由的基本信息。
基本结构:scheme://host/path?arg1=aa&arg2=bb
比如:pluto://charon/im/session?id=123&name=ginhoor
分解后 scheme:pluto,host:charon,path:/im/session,params:{id:123,name:ginhoor}
为了方便客户端使用,增加特定参数(不同的平台会使用不同的字段):
- vcClassName,iOS的视图控制器类名。
- paramsClassName,视图控制器参数接收器的类名。
- openType,视图打开方式(模态打开或者导航器打开,是否带有动画)。
- isPrivate,指定该规则是否对外使用,如果为False,则只有客户端内部发起才能成功。
为了增强规则的可读性,还增加了几个参数:
- demo,填写用于演示的URL字符串。
- remark,填写用于描述该规则功能的字符串。
如下,最终将这些规则整理程JSON文件,统一管理维护。
{
"version":"1.0.0",
"rules":[
{
"iOSVCClassName":"MCGlobalCategorySearchVC",
"iOSParamsClassName":"MCGlobalCategorySearchVCInputParams",
"params":{
"type":1,
"keywords":"kw",
"fromVC":"IMSessionVC"
},
"host":"route",
"path":"/home/category_search",
"demo":"daqun://route/home/category_search?type=1&keywords=kw&fromVC=IMSessionVC",
"isPrivate":0,
"remark":"MCGlobalCategorySearchVC"
}
]
}
路由规则配置(Rule Configuration)
维护与管理客户端整个路由规则的配置。
- 客户端路由、HTTP Host白名单
- 公域Scheme、私域Scheme白名单
- 用于客户端生成路由URL的公域Scheme域与客户端路由Host。
客户端启动时,会将本地预埋的JSON路由规则加载到客户端中。同时,需要为服务端提供配置下发方案,当服务端推送新的路由规则时,需要对本地路由规则进行更新。
最后需要为路由规则创建不同字段的索引,优化路由的解析耗时。
URL跳转管理模块 (Open URL)
URL分为三种,一种由客户端生成的本地URL,一种客户端以外途径生成的远程URL了,这两种可以通过路由规则中的isPrivate可以对URL的入口进行权限控制。最后一种是比较特别的HTTP URL,是纯HTTP的请求链接,但除去Scheme与Host,是可以与配置中的路由规则对应的,这也是一种有效的URL。
如果使用客户端路由URL生成二维码,用户当微信扫了这个二维码,而手机上没有安装我们的客户端,那么这次URL唤醒动作将失败,但如果使用中转页面的URL,微信扫了二维码,中转页面会判断是否可以直接唤起我们的客户端,如果不行,则可以引导用户进行下载。
所以HTTP URL这种形式,是为了在生成业务分享二维码时,可以使用中转页面的URL,同时中转页的URL维护规则又与客户端路由规则相兼容,做到最大程度的复用。
在客户端内部的模块间通信时,可以用反射模块直接通信(比如,注销IM用户,跳转会话界面)。也可以根据路由规则,先组装成URL字符串,经业务模块传递后,再后路由模块解析跳转(比如通过卡片消息转发H5分享路由地址)。
最后,如果URL无法通过合法性校验时,路由模块会调用系统的OpenURL来接管后续操作。