【ArchSummit】如何通过AIOps推动可量化的业务价值增长和效率提升?>>> 了解详情
写点什么

有赞美业接入智能 POS 的架构演进之路

  • 2020-05-17
  • 本文字数:4293 字

    阅读完需:约 14 分钟

有赞美业接入智能 POS 的架构演进之路

一、背景

众所周知,Android 是一个开源的系统,从它诞生的那一刻起,Android 系统被逐步应用在了各种各样的硬件设备中。随着移动互联网和移动支付的兴起,传统 POS 机也开始逐渐被 Android 系统加持下的智能 POS 机所取代。


有赞,是一家商家服务公司。我们帮助每一位重视产品和服务的商家私有化顾客资产、拓展互联网客群、提高经营效率,全面助力商家成功。为了满足商家日益丰富的支付渠道,尤其对于刷卡能力的需求,有赞的多个 App 均对接了智能 POS 机这一硬件。


随着业务的快速发展,笔者所在的有赞美业共陆续对接了多个厂家的不同类型的智能 POS 机硬件。如何更好地接入各式各样的 POS SDK 成为一个摆在我们面前的问题。


本文将按照时间线的顺序,结合在对接过程中的思考与总结,回顾我们 POS SDK 对接方案的演进过程,希望能够带给大家一定的启发。

二、演进过程

2.1 阶段一:直接集成 POS SDK ,快速满足需求

在业务快速发展的早期阶段,快速上线满足需求是首要考虑目标,且当时也只有一台 POS 机对接的需求,因此我们选择直接在现有代码中集成 POS SDK 的方案。

2.1.1 项目结构

2.1.2 方案分析

首先这个方案的优势很明显,对接成本低,满足快速上线的需求。结合当时的背景情况,选择这个方案也是很自然而然的。


然后相对的,这个方案的问题也非常的明显:


  • 业务代码与 SDK 逻辑耦合严重。

  • 未能抽象设备能力,拓展能力差。

  • 其他业务方无法复用对接逻辑,导致重复工作量。


试想一下如果这个时候又有台新设备需要对接,那工程代码中肯定会充斥着各种 if-else 判断来区分设备类型调用不同的 SDK,这简直就是一种灾难!


所以,在此方案上线运营的同时,我们就开始着手改进方案以解决这些问题。

2.2 阶段二:抽象设备能力,组件化改造

要解决阶段一的问题,其实就是要解决两个关键问题:拓展能力和复用能力。


对于这两个问题,最简单的做法就是对设备的能力进行抽象,在此基础上针对不同 POS 设备进行实现,封装成一个公共硬件库,供有赞 App 共同使用,也就是进行组件化改造。

2.2.1 项目结构

2.2.2 方案分析

经过组件化的改造,整个对接方案的拓展能力和复用能力都得到了极大的提升,能够满足一段发布上线。


但其实这个方案存在一些很容易被忽视掉的问题:


  • 随着接入设备越来越多,公共硬件库的体积将会急剧增加,进而导致业务 APK 的安装包也水涨船高。

  • 对于 POS A 设备来说它只需要自身所需的 POS A SDK 即可,其他 POS 设备的 SDK 完全是无用或冗余的。

  • 由于 POS SDK 之间实现的差异,很容易带来一些意想不到的问题,比如说依赖冲突、资源文件冲突等等。一定程度上影响业务 APK 的性能和稳定性。


针对这三个问题,我们开始寻找相应的解决方案。

2.3 阶段三:瘦身 + SDK 隔离,差异化打包

鉴于阶段二所存在的问题,当前阶段的改进方向已经很明确了:


  • 安装包瘦身

  • 将 POS SDK 相互隔离


众所周知,Android 原生就支持一种差异化打包方案: ProductFlavors


基于 ProductFlavors 方案,首先将硬件库的能力拆分开来,做到抽象能力与其实现分离,对于一台 POS 机来说其他 POS 机的实现不会再对自身造成影响,从而直接消除了性能和稳定性方面的影响,同时由于依赖的 SDK 变少了,业务 APK 安装包大小也得以显著减小。


关于 ProductFlavors 的基本使用和原理不在本文的讨论范围内,有兴趣的同学可自行查阅相关文档。

Android 官方文档

2.3.1 项目结构

2.3.2 方案分析

差异化打包方案很好地解决了阶段二所发现的问题,但实际上最终这个方案并未被采纳,因为这个方案在解决老问题的同时,却也带来了新的问题:


  • 如果业务 APK 使用了热修复能力,那么此时将会存在多个热修复的基准包,在每次热修的时候就需要针对每个差异包生成对应的补丁文件,而且以后每对接一个新的硬件就会多再一份工作量,想一想还是很感人的。

  • 很多第三方 SDK 初始化所需的 id 、key 等配置信息与包名绑定,所以如果你的差异化打包配置了不同的包名,那么还必须重新申请新的配置信息。如果你对接的服务还依赖后端服务,比如推送服务端,那么相应的后端也需要同步更新。


可以看到一旦采用了这个方案,那么后续的维护成本将成为一个不可回避的问题。因此我们迫切需要再找办法来解决后续的维护成本问题。

2.4 结局:分手快乐!

在寻找维护成本问题解决方法的同时,我们重新审视了前期对接方案以及线上版本的运营情况,观察到如下两个情况:


  • 厂商提供的 POS SDK 很少有版本变更,基本是一个版本用到底

  • 业务方一旦完成 POS SDK 对接,那么对接的核心逻辑也很少会有改动


以此为背景,那么既然 POS SDK 部分的后续变更几率很小,那么业务代码和 POS SDK 部分的代码如果能够独立开来进行维护,也就是说将 POS SDK 及其接入逻辑抽离出来,以独立 APK 的形式为业务 APK 提供能力,这样既能满足拓展能力、SDK 隔离等方面的需求,又没有了阶段三方案后期的维护之忧。


按照这个思路,我们将现有的工程拆分为两个独立的 APK :


  • 业务 APK:包含业务代码

  • 服务 APK:包含 POS SDK 及其对接逻辑


那么剩下的问题就是这两个 APK 要如何进行通信?


讲到这里,自然就要提到 Android 的进程间通信相关的知识了。


摘自互联网:


那么经过技术选型,AIDL 这种方式显然更加符合方案的需求,于是我们在此基础上进行尝试,AIDL 方案应运而生。


关于 AIDL 的基本使用和原理不在本文的讨论范围内,有需要的同学可执行查阅相关文档。

Android 官方 AIDL 技术文档

2.4.1 方案图示

2.4.2 方案分析

2.4.2.1 方案说明

AIDL 是一种 C/S 结构的模型,在这个方案中,服务 APK 作为服务端向客户端也就是业务方 APK 提供能力,二者之间通信的关键枢纽就是自定义的 AIDL 通信协议。


而通信协议将会放在 support base 之中,同时提供给服务 APK 和 业务 APK ,然后二者各自只需要关注的自己所负责的内容即可:


  • 在服务 APK 中,将 POS SDK 能力按照通信协议封装为统一的对外接口。

  • 在业务方 APK 中,也无需再关心当前 POS 硬件类型,只需要按照定义的通信协议使用相应的能力即可。


至此,业务代码与 POS SDK 彻底解耦,后期维护彻底分开,达到了方案预期的目的,同时得益于二者的独立,多业务方可以共用同一个服务 APK 所提供的能力,有效的避免了重复对接工作量,无缝对新的设备提供支持。

2.4.2.2 不同能力的调用时序差异

针对 POS 设备所提供的不同能力,从资金安全的角度出发,可以将 POS 能力划分为交易能力和其他能力。二者最主要的差别在于:


  • 在使用交易能力的时候,调用结果不直接返回给业务 APK ,而是经由业务 APK 自行轮询得到后端支付状态变更。

  • 在使用其他能力的时候,比如打印能力,由于其场景的低敏感度,调用结果直接通过回调告知业务 APK 。


2.4.2.3 全新的 POS 对接方式

与 POS 厂商或第三方进行过对接的同学可能都会有一个感受:我太难了!


  • SDK 和文档是两个东西,来回的沟通和确认,效率极为低下

  • 负责接入的同学死活运行出错,外部对接人员却表示反正他们没问题

  • 负责接入的同学天天加班调试,外部对接人员不慌不忙


但是现在,得益于 AIDL 协议层的存在,这个方案提供了一种全新对接方式的可能:


  1. 业务方提供 AIDL 协议层( support base )给 POS 厂家或第三方进行实现并完成调试

  2. 设备中安装有赞 App 即可使用 POS 设备能力


想象下,如果能这样对接,它不香吗!


三、回顾与总结

没有最优的架构,只有最合适的架构,一切系统设计原则都要以解决业务问题为最终目标,脱离实际业务的技术情怀架构往往会给系统带入大坑,任何不基于业务做异想天开的架构都是耍流氓。


回顾整个方案演进过程,可以看到我们每一个阶段的方案都是基于当时的业务现状进行设计,同时随业务发展需求逐步去改善的。


从技术角度来看,改进的基本思路都是围绕着耦合程度、拓展能力、接入成本、维护难度这四个维度进行的。


为了让不同阶段的方案能有一个更直观的对比,这里有个表格:


对比直接集成硬件库差异化打包AIDL
耦合程度
拓展能力
接入成本逐渐增高逐渐增高
维护难度逐渐增高逐渐增高


综合来看,无论从哪个维度来说 AIDL 都是目前最佳的实现方案。


  • 业务 APK 和服务 APK 独立,耦合程度极低,分别进行发布管理

  • App 安装包大小、性能及稳定性不会到受到 SDK 的影响

  • 业务方无需关心当前 POS 硬件,只需要按照统一的协议去使用能力

  • 拓展新设备无压力

  • 对于新设备的对接,只需实现其对应的服务 APK 即可

  • 接入成本相比更低

  • 一个业务方完成了对接,其他业务方均将受益

  • 全新的对接方式:由第三方或 POS 厂家实现

  • 维护难度:完成接入后的低维护成本

  • 专心维护业务 APK ,无需过多关心服务 APK 的维护

  • 不影响现有 App 现有基础能力,如热修复、推送等

四、探索与展望

4.1 动态加载技术和插件化方案可行分析

在 AIDL 方案中,业务方 APK 能够正常使用能力的前提是服务 APK 被同时安装在设备中。这就涉及到未安装和版本不符等版本管理的问题,而这些问题毫无疑问会大大增加商家的使用难度,影响商家的使用体验。


针对如上问题,大家可能会想到利用动态加载技术,动态加载 APK 或其他形式的插件,来实现服务 APK 免安装、动态下发替换的效果。

4.1.2 插件化方案流程图

4.1.3 终极解决方案?

单看这个流程图,这不就是一直以来追求的终极解决方案了吗?


桥豆麻袋!


诚然,从技术角度讲,插件化方案能够满足我们所有的需求,支持动态配置下发,看起来都是那么美好,但这里不得不先向你泼一盆冷水。


众所周知,自 Android 9.0 (P)版本起,Android 系统将开始限制调用私有的系统 API 的行为,且这个限制还会随着后续版本更新逐步进行加强。


相关信息可以参考这篇官方文档:

针对非 SDK 接口的限制


很不巧,市面上耳熟能详的多个热门插件化框架或多或少均受到了影响。


为了验证可行性,笔者对多个插件化方案进行了实际调研,然后发现哪怕是其中宣称已适配了 Android 9.0+ 版本的插件化框架,在实际使用的时候依然是问题重重,比如:


  • gradle 版本大多还停留在 3.x

  • 基本都不支持 androidx

  • 文档更新滞后, issues 页面无人应答, merge request 无人处理


因此目前并不建议采用使用插件化的对接方案,毕竟使用各种“黑科技”绕过官方限制的做法,终究是背道而行,是不得长久和不可持续的。

4.2 展望

在未来的规划里,我们将进一步地完善方案,尝试形成一个更加通用的接入方案,能够覆盖到 POS 机以外的其他硬件,降低整体的硬件能力接入成本,为商家提供更好的统一的使用体验。


作者介绍


葫芦娃


资深 Android 开发工程师


一个快乐的中年油腻男


常年浪迹于互联网江湖,身怀多年 Android 开发经验,现投身于有赞美业移动团队任职 Android 开发工程师,主要负责有赞美业 Android 平板、手机、POS 机、电话机等客户端的开发及维护工作。


本文转载自公众号有赞 coder(ID:youzan_coder)。


原文链接


https://mp.weixin.qq.com/s/AovOCP90PxVNggipN99uqQ


2020-05-17 10:051490

评论 2 条评论

发布
用户头像
多谢分享!赞同最终选择的方案选择。有一个问题,对于服务Apk来说,也需要面对支持不同SDK来支持不同POS硬件的问题。最终方案把这个问题从业务Apk转到了服务Apk处理
2020-05-23 11:28
回复
服务APK结合差异化打包是不是可以解决支持不同SDK支持不同POS硬件的问题?
2022-03-26 17:06
回复
没有更多了
发现更多内容

从数据中台到AI中台,企业到底要建什么中台?

脑极体

ARTS Week8

丽子

ARTS-week-2

saddamwilson

ARTS 打卡计划

你为什么还在用存储过程?

架构师修行之路

数据库设计 架构设计

原来你是这样的B+树

Java技术宝典

B+树

HTTPS证书过期导致的故障

焦振清

运维 https SRE 服务故障 证书过期

多省市出台关于区块链人才引进的计划

CECBC

新基建 区块链技术

2.2.1 类反射 -《SSM深入解析与项目实战》

谙忆

以区块链为基础 通证经济是下一代互联网的数字经济

CECBC

区块链 落地应用

List 和 Map 的排序

一盐难进

Java

服务器与普通电脑的区别?

德胜网络-阳

# spring boot自定义线程池进行异步调用

一盐难进

Java

机器学习算法之——K最近邻(k-Nearest Neighbor,KNN)分类算法原理讲解

迈微AI研发社

学习 算法 KNN K聚类

区块链+收藏品,全球三种典型应用路径的差异化

CECBC

区块链 应用价值

troubleshoot之:用control+break解决线程死锁问题

程序那些事

Java JVM 死锁

真正的勇士,会跨过六道裂谷,奔向云与AI的彼端

脑极体

JDK1.8新特性(七):默认方法,真香,开动!接口?我要升级!!

xcbeyond

接口 新特性 JDK1.8 默认方法 JDK1.8新特性

踩坑记 | Flutter升级影响了NestedScrollView?

哈利迪

android

Go: 互斥锁和饥饿

陈思敏捷

mutex Go 语言

基于 grpc,protobuf搭建 server/client模型通信

是老郭啊

知路,然后智行远;懂行,所以万业兴

脑极体

LeetCode题解:24. 两两交换链表中的节点,递归,JavaScript,详细注释

Lee Chen

大前端 LeetCode

机器学习算法之——卷积神经网络(CNN)原理讲解

迈微AI研发社

学习 算法 卷积神经网络 CNN

Kafka处理请求的全流程解析

yes

kafka 面试 后端 消息队列 源码解析

如何对 ElasticSearch 集群进行压力测试

白宦成

elasticsearch ELK Elastic Stack

nested exception is java.lang.IllegalStateException: refreshAfterWrite requires a LoadingCache异常解决

谙忆

DevOps 技术栈

柴锋

Linux DevOps 运维 敏捷 Shell

区块链跃升各国创新战略

CECBC

新基建 国家战略 区块链标准

如何设计实现一个证书加密签名工具包

三尾鱼

数据采集能力受限?企业数字化运营如何迈出第1步

易观大数据

2.1.2 类加载器的工作原理与自定义加载器 -《SSM深入解析与项目实战》

谙忆

有赞美业接入智能 POS 的架构演进之路_移动_葫芦娃_InfoQ精选文章