47

【架构拾集】基于混合应用架构的跨平台实时聊天应用

 5 years ago
source link: http://www.phodal.com/blog/architecture-design-build-instant-message-application/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

基于 Web 与混合应用框架的架构,设计一个实时聊天工具,并不是一件轻松的工作:

  • 实时性 。在创建应用的时候,我们遇到这么一个质疑:你们的聊天应用做不到很好的体验。细问之下,找到了原因:他们之前开发的 IM 是基于类似 Ajax 轮询/WebSocket 的方式开发的,网络质量差,或者不活跃时,就无法保证实时性。
  • 流畅度 。在开发的时候,我们还不得不考虑流畅度的问题,聊天应用是一个带有大量消息的应用。一旦消息过多,可能会导致消息滑动不流畅的问题。为了支持获取历史消息,及对大量消息的处理,我们进行了大量的研究。从性能上来看,Web 应用的长列表不可能像原生应用那么好。
  • 用户体验 。在开发完的时候, 当业务、开发、测试人员都习惯了微信,那么就会有一种先入为主的想法——觉得这个地方应该这么做,每个地方应该和微信等保持一致,而这些功能并没有在最先的规划中。这一点尤为重要,当我们在做一个类似于 blabla 的应用,应该以既定业务功能为主,其它的需求都是未协商的新需求。

不论怎样,让我们来看看这个应用是怎么设计的吧。

背景

我们所在的团队,是一个框架(混合应用框架、平台化框架、Web 前端框架)的开发团队。业务方需要基于一个聊天的 SDK,开发一个聊天应用。而这个聊天应用,不仅仅是一个业务方所需要的。其它的业务方,都有可能针对于这个聊天应用有定制需求。

技术远景

作为一个框架的底层提供方,我们预期我们开发的聊天应用:

  1. 接近微信、内部聊天工具的使用体验
  2. 方便第三方进行修改,而不需要为每个使用方定制

技术架构

在这个聊天应用的设计过程中,我既承当了部分的业务分析职责,又作为一个 “架构师” 设计整个系统的架构。当然了,我主要做的部分是前端 UI 部分的设计,及对应的与原生部分的接口设计、参数传递等。与此同时,在早期的 POC 阶段(概念验证),为了验证理论的可能性,我还作为一个 Android 开发,测试了一系列的接口。

对应的系统架构设计

  1. 使用 Web 技术来开发 IM 的前端页面,使用原生技术来提供聊天支持。
  2. 将 IM 作为一个独立的 Web 容器抽离出来。修改后,可以替换相应的 Web 资源目录即可;也可部署在远程的服务器上。
  3. 通过原生存储来实现容器间的参数传递。
  4. Angular + Lazyload,可以直接复制、粘贴到其它 Angular 应用中。

针对于第二点做一个补充说明。事实上,这已经是我开发的第二个 IM 应用了。在上一个 IM 应用中,我犯了一个不大不小的错误,将 IM 代码和业务代码混合在一起。导致了,这个 IM 部分的功能,无法在后期直接抽离出来。我写第一个版本的 IM 应用时,由于当时的技术限制(只有 Android 开发能力)、背景限制(只开发应用),而没有考虑到二次复用的问题。不过,由于使用的是 Angular.js,也不得不再次用 Angular 实现一个版本。

适用场景

对应的,写一个简单的电梯演进:

关键因素 描述 对于 想集成聊天应用的团队 我们的 跨平台聊天应用 是一个 可定制的混合聊天应用方案 它可以 方便地集成与修改 但他不同于 基于 Web 的聊天应用 它的优势是 实时的聊天感受、流畅地用户体验

C4 Model

TBC

领域与技术模型

从业务上来看,可以分为这么几部分:

发送文本消息。文本消息的发送,实际上是最简单的功能。只需要调用一个原生部分的接口,我们也可以实现相应的功能。过程中,唯一比较复杂的地方是, 对于消息框的高度的处理 。这里的文本框,有最大高度和最小高度的限制,即最小是一行,最长是 3 ~ 4 行。当输入的内容是一行的时候,显示的是一行的高度,二行的时候显示的是二行的高度,三行、四行及以上的时候,显示的是三行的可见区域。

文本消息展示。文本消息的接收属于其中最简单的一部分,并不涉及过于复杂的处理逻辑。

发送语音消息。语音消息是几种消息中最复杂的部分,它涉及到长按输入按钮、变换输入按钮、计时、语音动画、上滑取消、下滑继续、松开发送等一系列的业务。在这几个部分中,最复杂的地方便在于长按和滑动相关的计算。

语音消息播放。由于 SDK 本身的限制,所以播放语音的时候,需要交给原生来处理。于是,前端只需要播放相应的动画即可。然而,过程中有一个比较大的坑是,消息会有一个下载的过程。

发送图片消息。关于图片有一些基本的业务需求:

  1. 图片是以附加工具栏的方式,由下往上滑动。为此,需要一个简单的 toggle 来显示和隐藏。
  2. 图片的类型分为两类:一个是从相册中选取,一个是拍照获取。

图片消息播放。由于 iOS 平台对于文件的访问限制,因此提供给前端的是 Base64 编码后的缩略图,点击后又需要显示原来的大图。过程中,即需要获取缩略图的接口,又需要展示大图的接口。

从 UI 来看,我们可以推算出基本的前端数据模型:

  • 发送状态
  • 消息时间
  • 消息内容
  • 发送/接收者
  • 消息负载
    • 图片的缩略图 ID(可选)
    • 图片的大图 ID(可选)
    • 语音消息 ID(可选)
    • 语音时长(可选)

技术实现

前端

从技术上来看,前端比较麻烦的地方在于各个组件的研究。而这时呢,我们使用使用的前端框架是 Angular:

  • 无限滚动。Angular 7 本身是自带的,不过当前(2018.03)还是 ngx-infinite-scroll 好用,其中的 infiniteScrollDistance 需要动态计算。
  • 虚拟滚动。使用的组件是 ngx-virtual-scroller ,导是没有遇到什么大坑。
  • 长按输入。网上找了一个 longpress 的 directive,添加了对 touchmove 事件的支持。
  • 自动调整输入框。使用提 ngx-autosize ,不过稍微修改了一下。

然后,比较坑的地方就是适配 Android 和 iOS 机型上的问题了。

JavaScript Bridge

为了保持有消息的状态,即创建、发送、取消,需要保持有原有的回调函数。在 Cordova 中,需要通过 keepCallback 来保持这个回调,以多次调用相就的返回值。

同时,值得注意的是,通过 promise 的方式,是不能在一个函数中多次返回结果,因此需要通过 callback 的方式从 JavaScript 获取结果。而在 Angular 框架中,又有外部 callback 需要手动 DetechChange 的问题。

原生

普通的 SDK 封装。

麻烦的地方在于需要多提供一些这么事件:

  • 键盘的显示和隐藏
  • iOS 的键盘高度

针对于 iOS 的键盘高度问题,主要是调用第三方输入法(如搜狗)返回的 scrollTop 不对的问题,便需要在原生中处理这个高度。

踩坑经验

iOS 的键盘问题

输入框使用的是 position:fixed ,但是在 iOS 上会有遮挡的问题。网上有太多的解决方案了,最后我们采用的是:

setTimeout(function(){
    document.body.scrollTop = document.body.scrollHeight;
},300);

iOS 的 WebView 滚动问题

iOS 上需要 -webkit-overflow-scrolling: true 来解决 滚动与回弹效果的问题。这个属性用于控制元素在移动设备上是否使用滚动回弹效果.

iOS 的键盘收起影响元素问题

这是另外一个坑,在键盘收到的时候,底部的某个按钮莫名其妙的消失了。只能再加一个 setTimeout 100 来显示出来。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK