App 进程都被杀了,外卖进度为什么还能继续更新?

文章封面

做外卖进度实时展示的时候,最开始的思路很简单:App 里开个轮询,每隔几秒请求一次进度接口,然后更新实况窗。

跑了一段时间发现问题:用户把 App 杀了,轮询停了,实况窗就不动了。用户看着进度条卡在那里,还以为外卖不送了。

这时候才意识到:本地更新依赖 App 进程,进程死了就停了。要让实况窗在 App 不存活的时候还能更新,得把更新链路转移到服务端,用 Push 来推。

一、本地更新和远程更新有什么区别

先把概念理清楚:

类型更新方式App 存活时App 被杀后
本地更新App 进程轮询正常更新停了
远程更新服务端 Push正常更新继续更新

本地更新就是 App 自己在跑,进程活着就能更新。远程更新是服务端通过 Push Kit 把新状态推给系统,系统直接更新实况窗,不需要 App 进程活着。

二、实况窗的三个阶段

一个完整的实况窗生命周期,分三个阶段:

阶段操作谁来做
创建创建 LiveView,生成 IDApp 做
更新更新进度状态本地 or 远程
结束停止 LiveView本地 or 远程

创建阶段肯定是 App 做的,因为用户在 App 里下单,点开始配送。更新和结束阶段,可以本地也可以远程。

App 活着的时候,本地更新没问题。App 被杀了,就得靠远程 Push 更新。

这段代码解决什么问题: 创建实况窗。
文件: liveview/LiveViewManager.ets
用途: 创建实况窗
接入位置: 用户下单后

import { liveViewManager } from '@kit.LiveViewKit';

async createDeliveryLiveView(orderId: string) {
  const options = {
    liveViewType: 'delivery',
    title: '外卖配送中'
  };
  
  const result = await liveViewManager.createLiveView(options);
  const liveViewId = result.liveViewId;
  
  // 把 ID 存到服务端,后面远程更新要用
  await this.saveLiveViewIdToServer(orderId, liveViewId);
}

这里最关键的就是:创建完的 LiveView ID,一定要存到服务端。后面服务端要靠这个 ID 来更新对应的实况窗。

系统架构图

三、LiveView ID 和订单 ID 为什么不能混用

很多人搞混了:LiveView ID 是系统生成的实况窗 ID,订单 ID 是业务自己的订单 ID。这两个不是一回事。

服务端要更新实况窗,得用 LiveView ID,不是订单 ID。你把订单 ID 传给服务端,服务端不知道更新哪个实况窗。

正确的做法是:创建的时候把 LiveView ID 和订单 ID 绑定,存到服务端。服务端更新的时候,根据订单 ID 找到对应的 LiveView ID,再用这个 ID 更新。

四、远程更新的完整链路

远程更新的完整链路是这样的:

  1. 服务端业务状态变化(骑手取餐了);
  2. 服务端通过 Push Kit 发消息;
  3. 系统收到 Push,更新实况窗;
  4. 用户看到进度变了。

整个过程不需要 App 进程活着。系统直接帮你更新了。

API 26 还支持实况窗通过 Push 下载网络图片。比如骑手位置、商品图片,都可以直接推过去。

五、结束阶段为什么要一致

还有个容易踩的坑:结束阶段不一致。

情况问题
服务端订单完成了实况窗还在显示配送中
用户手动关了实况窗服务端还在继续推

正确的做法是:服务端订单完成了,要远程停止实况窗。用户手动关了,也要通知服务端不要再推了。不然就会出现:订单都结束了,实况窗还在转。

六、几个容易踩的坑

第一个坑:LiveView ID 和订单 ID 混用。服务端找不到要更新的实况窗。

第二个坑:只存客户端状态。App 杀了,状态就丢了。

第三个坑:App 被杀后仍依赖本地更新。轮询停了,实况窗不动。

第四个坑:服务端已经结束但实况窗仍存在。订单都完成了,进度条还在转。

第五个坑:更新频率超过限制。推太频繁,系统限流。

运行效果图

这次做实况窗最大的体会是:实况窗不是 App 里的一个组件,是系统级的东西。App 活着的时候本地更新,App 死了就要靠服务端远程推。创建、更新、结束三个阶段的状态一定要一致,不然就会出现各种对不上的情况。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐