前菜 -- 科普
是什么
移动开发中所说的schema概念是指资一种自定义的资源标示符,格式是[scheme:][//authority][path][?query][#fragment]
,相当于绝对路径版的URI,实在通俗的说,URL�的http(s)替换成自定义的协议头,就是schema。
举例一个国际航线搜索的schema可以是这样(具体协议命名脱敏)
Paste_Image.png
有什么用
schema主要用作异构体系之间的低耦合互操作方案,每个应用/模块都可以自定义的协议头。借助schema可以是实现比如touch页中打开特定APP,某APP触发其他APP的功能,或是用于类似Qunar这样组件化架构的APP内各组件间互操作。
项目背景
做为Qunar移动端业务组件间以及与内外互操作的主要机制,单机票业务所提供的schema数量就有数十个。早期的做法是“见招拆招”,每加一种schema就加一个判断,在schema规模扩大后这种做法的维护效率会快速下降。
最初的schema处理方式(伪代码)
String type = schema.getLastPathSegment();
...
if(type == 'search') {
String flightType = map.get("flightType");
if(flightType == 'oneway') {
...
}
if(flightType == 'round') {
...
}
if(flightType == 'multi') {
...
}
}
if(type == 'orders') {
...
}
同时,由于缺少有效的文档同步机制和测试用例,内部的wiki已经不能100%保证完整,如果PM/QA/schema调用方同学需要确认app到底支持什么schema,有什么样的参数,大多时候竟然需要肉查代码。
总的来说有三方面要改进:
- 实现不够先进(理解修改使用麻烦)
- 信息不对称(文档和编码�割裂,且文档缺失不完整)
- 没有简单易用的测试机制来验证schema相关代码的增删改
改造成啥样?
最简化的模型
所谓的schema处理其实就是干这么个事情,给定一个schema,找到并执行一段代码。
Paste_Image.png这事儿看上去实在太简单了。。。不过也能帮我们看的更透彻,其实改造的关键是实现一个schema到对应执行点的匹配分发机制。
要易维护、要直观、要schema支持范围可确定性......首先想到用某种配置作为匹配的依据
Paste_Image.png技术改造类项目,如果仅是让大家开发起来爽+快,或许是最低的标准,为了让这个改造能在开发以外的方面产生协同效应,我们要考虑借此项目一并解决文档缺失和难于测试的痛点。
辣么期望达到的效果是:
- 基于某种配置自动识别schema�该由谁来处理(基于配置的匹配分发)
- 能够基于配置生成文档(自解释性)
- 能够加载schema测试样本源并对接到我们的自动化测试系统
Finally, 完整的架构
Paste_Image.png如何使用?超级简
Paste_Image.pngQuery/Test Tools保真效果页面(Powered by Adobe XD)
点我看Showcase
features:
- 查看当前App受支持的所有schema
- 从schema样本源中筛选不被支持的schema
主要角色、行为含义解释:
- schema配置信息:包括schema表达式、分发执行的落脚点、文档说明和样例schema等。
- Entry:schema的入口,对安卓来说是
manifest.xml
中定义的intent-filter
对应的组件 - Registration:schema规则注册器,提供规则的装载注册和匹配查找能力。
- Dispatch:schema分发器,接收schema并使用Registration做查询和分发处理。
- Producer:执行器包装�,每个schema配置在运行时层面都对应一个Producer,将具体的反射、传参等可能对规则变化封闭在内部。
- SchemaHandler:执行各个schema的实现类
- dump:导出app所支持的schema配置信息
- Test/Query:查询/测试schema是否被当前app支持并提供反馈结果
- Document:可阅读的schema说明文档
数据事件流转说明
- schema通过intent-filter传递到Entry组件(activity或receiver等)
- Entry中初始化Dispatch,并将schema和Bundle交付给Dispatch做分发
- Dispatch通过Registration查询到Producer并传入uri和bundle
- Producer将schema和bundle做参数提取成字典结构,构造SchemaHandler实例并执行。
关键细节实现思路
如何匹配schema
schema可以看成一种表达式,我们设计了一个表达式规则来判定指定的schema是否和我的schema表达式规则兼容。
Paste_Image.png分为三部分:
- 第一部分
protocol+host(也叫authority)
对于每个业务组件是确定的; - 第二部分是path,不同含义的schema的path一定不同(比如航班搜索是search,航班动态是status),相同的path可能会有多个行为;
航班搜索-单程:sopaco://flight/search?module=oneway&depCity=BJS...
航班搜索-往返:sopaco://flight/search?module=roundway&depCity=BJS...
航 班 动 态: sopaco://flight/status?depCity=BJS...
- 第三部分是模糊匹配的参数区,我们总结了目前用到的几种特定参数值如枚举值、日期、数字、必须字段、可选字段。
模糊匹配值表达方式:
Paste_Image.png
如何转化为配置
配置信息中除了要包含schema表达式,还要有对应的函数执行标识,考虑到要能自解释生成文档,还需要携带本身的说明信息。我们选择用java interface + annotation来作配置载体。
Paste_Image.png其中X.class
是SchemaHandler
的实现
public interface SchemaHandler {
void handle(SchemaContext context, Uri originalSchema, Map<String, String> params, Bundle extra);
}
这个interface不需要实现类也不需要实例化,仅利用annotation语法做配制标注作用并享受语法检查的便利,每个函数名称命名随意,在@Schema
中声明schema表达式规则和对应的实现类。
读取配置
�设计一个Registration组件(类似于协议注册中心的概念),来加载业务线的schema分发规则配置是ISchemaProtocol.class
,核心方法是装载配置的函数setup
和match
匹配的schema处理器。
setup
函数实现
public <Protocol> void setup(Class<Protocol> contract) {
Method[] methods = contract.getMethods();
for (Method method : methods) {
Schema schemaConfig = method.getAnnotation(Schema.class);
Class<? extends SchemaHandler> executor = schemaConfig.executor();
Producer producer = new Producer(method, executor);
SchemaPattern pattern = new SchemaPattern(schemaConfig, producer);
SchemaDocument schemaDoc = method.getAnnotation(SchemaDocument.class);
pattern.setDocument(schemaDoc);
patterns.add(pattern);
}
}
衍生物-自解释文档
所谓自解释文档,就是app应该能把自己能支持的schema都有哪些、参数都是啥、说明信息等dump出来并以某种形式展示,我们在ISchemaProtocol.class
上,增加了@SchemaDocument
这样一个标注,和@Schema
不同的是这个要区分production构建环境,在gradle里配置两套SchemaDocument的实现,release下是source级别的标注,这样线上包编译期间就能丢掉SchemaDocument
,仅在beta/dev做RUNTIME标注。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
public @interface SchemaDocument {
String title();
String content() default "该配置无说明";
String sample() default "";
}
配套的schema toolkit页面,能看到app中受支持的所有schema表达式。
Paste_Image.png衍生物-对接自动化测试
自动化测试要做的是提供一份schema用例样本,app能识别出其中所支持/不支持的schema,对支持的schema,并且通过QA团队之前搞的自动化图像对比工具,在每次发版前对比新旧版本相同schema跳转后的页面切合度是否一致。
效果请点击看Showcase(based on Adobe XD)
现已拆分为QunarSchemaDispatcher库项目
该项目已经拆分出库版本供其他业务线同学使用
http://gitlab.corp.qunar.com/mobile_flight_adr/qschemadispatcher
业界类似方案和区别
对schema处理,目前在业界开源放出来的主流方案有Airbnb的DeepLinkDispatch和阿里巴巴的ARouter,这俩的思路和做法几乎是一样的,通过在schema落地组件上直接加annotatio实现点对点的"链接"并且支持依赖属性注入配置,为了达到对直连组件的零侵入性,对标注的处理使用了APT技术在编译前生成了Navigator Delegate代码而非运行时反射。
DeepLinkDispatch和ARouter都是现阶段开源出来的优秀方案,如果是全新的项目无脑选就是了。但不适用于像我们这种已经积累了几十个schema业务处理繁杂带包袱需要在短期优化核心痛点的项目,如果选用就意味着完整的重写,产生很高的测试验证成本。而我们的这个方案只需修改整个schema处理链中前置的匹配和分发环节,最终的schema业务逻辑处理部分(SchemaHandler)只是搬代码,解决关键问题同时降低重构风险。
我是无法屏蔽的广告位
我们在国内顶尖的在线旅游平台去哪儿网的主干力量机票事业部中最懂C端的用户产品部,欢迎各路移动端精英加入。
职位描述:
1.负责移动端技术分析和设计工作,解决技术难题和技术攻关突破,保证app稳健、高效运行。
2.针对复杂业务进行架构和重构编码,设计有效的技术方案并推动提高业务交付效率。
3.对移动端进行性能和质量优化。
4.感知业内技术变革趋势并合理运用到实际项目中。
职位要求:
1.2年以上Android应用开发经验,3年以上java开发经验。
2.精通java语言,在数据结构、算法、设计模式和架构上有扎实的技术功底。
3.熟悉主流的android应用设计思路、开发架构和框架
4.有性能调优经验,熟练使用常用tracer/profiler工具,能针对app性能和质量的相关性指标实施监控和改进方案。
5.对业内热点技术趋势如rx响应式编程、mvp/vm、APT/aspectJ、react-native泛前端等技术有深刻理解并在移动端的有实践经验。
5.具备有效的沟通能力、团队协作意识和工作责任心。
网友评论