鸿蒙实况窗开发:只会liveViewManager?巧用Push Kit才能告别用户投诉!

大家好啊~我是那个在代码海洋里扑腾了10+年的老水手,目前主业是"鸿蒙应用开发+Web全栈开发"双面间谍。

这些年写过的BUG能绕地球半圈,填过的坑能养活一个施工队,当然也攒了点有用的经验(毕竟吃一堑长一智嘛)。

平时最大的爱好就是把复杂的技术掰碎了、嚼烂了,做成普通人也能看懂的小甜点分享给大家。

如果你也喜欢折腾代码、踩坑、填坑,或者想找个人唠唠技术嗑,欢迎关注我一起交流~毕竟,独乐乐不如众乐乐,一起进步才是正经事!


上周夜里两点,我被一个报警电话吵醒。运营那边炸了锅:好几个用户投诉,打车打到一半,订单状态停在"司机已接单"死活不更新了。用户看不到车到哪儿,急得直骂街。

一查日志,好家伙,问题出在实况窗上。我们的小哥图省事儿,全程只用 liveViewManager 在App本地更新实况窗。结果用户切到后台刷了会儿短视频,进程一被回收,更新链就断了。司机那边都开到目的地了,用户手机上的实况窗还停留在十分钟前。

说白了,这就是掉进了 “本地更新依赖进程存活” 的大坑。今天咱就唠唠,怎么用 liveViewManager 和 Push Kit 打好配合,把这坑填得平平的。你想想,一个负责漂亮地"开场"和前台即时响应,一个负责稳定地"续命"和后台全局调度,这不挺合理的吗?


首先,咱得把事儿掰扯明白

看了官方文档,你可能觉得有两套东西都能管实况窗:liveViewManager 和 Push Kit。有点懵,对吧?其实啊,它俩是接力赛的关系,干的是同一件事(实况窗生命周期管理)的不同阶段。

liveViewManager (Live View Kit):这是客户端SDK。它的活儿是跟你App的UI线程紧密配合,创建第一个实况窗卡片,以及在App前台活跃时进行快速的本地更新。特点是快、即时。但它有个致命前提:你的App进程得活着,最好是在前台。

Push Kit:这是云端服务。它的核心能力是穿透。不管你的App是在后台还是被杀了,Push Kit的服务器都能通过系统通道,把更新指令推送到用户设备上,进而更新实况窗。它负责的是保活和全局状态同步。

所以,一个常见的误区是二选一。正确的姿势是:本地创建 + 云端推送更新。官方也是这么推荐的,为啥?为了可靠性。


关键抉择:这活儿该派给谁干?

更新方式 进程依赖 适用场景 生命周期限制 推荐优先级
本地更新
(liveViewManager.updateLiveView)
需应用进程存活 前台服务即时状态(如:倒计时、进度条实时刷新) 进程退出即失效 ★★☆
推送更新
(Push Kit)
无进程依赖 跨进程/离线场景(如:订单状态变更、物流轨迹更新) 最长8小时超时 ★★★

本地 (liveViewManager) 分支:连接"用户刚下单,APP在前台"、“短时、连续的状态微调(如进度条每1%更新)”、“APP始终在前台的特殊场景(如内置导航)”。

远程 (Push Kit) 分支:连接"APP转入后台或进程可能被杀"、“关键状态节点更新(如司机已接单->已到达)”、“最终结束实况窗”、“用户长时间未操作APP”。

翻译成人话就是

  • 创建时刻,用 liveViewManager。用户点完外卖,App还亮着,这时候立刻弹出"订单已接收"的实况窗,体验最丝滑。
  • 常规持续更新,优先用 Push Kit。特别是那些决定性的状态跳转,比如"商家已接单 -> 骑手已取餐 -> 已送达"。这些节点必须触达,不管App在干嘛。
  • 如果你的应用场景特殊,要求极高频率、极低延迟的连续更新(比如一个秒级的锻炼计时器),并且你能保证App前台存活,那可以用 liveViewManager 本地更新。但得小心系统频控,别更太猛。
  • 结束时刻,用 Push Kit。服务完成了,云端发个指令干净利落地结束它,最保险。

决策做完,咱们上代码看看具体怎么搞

实战 Part 1: 用 liveViewManager 漂亮地开场

假设我们做一个外卖取餐场景。首先,得检查用户有没有把咱们的实况窗开关给关了(设置里能找到),这是前提。

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

export class LiveViewService {
  // 1. 检查开关,这步不能省
  private static async checkLiveViewSwitch(): Promise<boolean> {
    return await liveViewManager.isLiveViewEnabled();
  }

  // 2. 创建一个取餐实况窗(强调文本模板)
  public async startPickUpLiveView(orderId: number, code: string, shopName: string): Promise<void> {
    try {
      const isEnabled = await LiveViewService.checkLiveViewSwitch();
      if (!isEnabled) {
        console.warn('实况窗开关被用户关闭,无法创建');
        return;
      }

      const liveView: liveViewManager.LiveView = {
        id: orderId, // 用订单ID作为实况窗唯一ID,方便后续更新关联
        event: 'PICK_UP', // 场景类型,必须和申请的场景一致
        liveViewData: {
          primary: {
            title: '餐品已备好',
            content: [
              { text: '请前往 ' },
              { text: shopName, textColor: '#FF0A59F7' }, // 强调色
              { text: ' 取餐' }
            ],
            clickAction: await this.buildWantAgent(), // 点击卡片跳回App
            layoutData: {
              layoutType: liveViewManager.LayoutType.LAYOUT_TYPE_PICKUP,
              title: '取餐码',
              content: code, // 取餐号
              underlineColor: '#FF0A59F7',
              descPic: 'resource://rawfile/coffee.png' // 旁边放个咖啡图
            }
          },
          // 3. 同步创建一个胶囊(状态栏显示)
          capsule: {
            type: liveViewManager.CapsuleType.CAPSULE_TYPE_TEXT,
            status: 1,
            content: `取餐码 ${code}`,
            icon: 'resource://rawfile/capsule_icon.png',
            backgroundColor: '#FF0A59F7'
          }
        }
      };

      const result = await liveViewManager.startLiveView(liveView);
      if (result.resultCode !== 0) {
        console.error('创建实况窗失败:', result.message);
      }
    } catch (error) {
      console.error('启动实况窗异常:', error);
    }
  }

  private async buildWantAgent() {
    // 构造点击实况窗后想要跳转的Ability信息,具体代码参考官方wantAgent文档
    const wantAgentInfo = {/* ... */};
    return await wantAgent.getWantAgent(wantAgentInfo);
  }
}

看这段代码,几个关键点:

  • id 很重要,它是后续云端推送更新的唯一依据。
  • event 要和你在AGC后台申请的场景对上
  • 顺手把 capsule (胶囊) 也创建了,用户不点卡片也能在状态栏看到关键信息。
  • 图片路径用 resource://rawfile/ 开头,别写错了。

好了,现在用户下单后,一个漂亮的取餐实况窗就弹出来了。但它还是个"宝宝",离不开妈妈(App进程)。接下来,我们得给它找个"云保姆"。


实战 Part 2: 用 Push Kit 当坚实的云保姆

Push Kit 的活主要在后端。流程是这样的:

  1. App 用 Push Kit 拿到一个设备标识 pushToken,并把它和刚才的 orderId(即实况窗 id)一起上传给你的业务服务器。
  2. 当订单状态在后台变化时(比如商家接单),你的业务服务器调用华为 Push Kit 的服务端API,发送一条更新消息。
  3. 这条消息会通过系统通道直接推送到用户设备,系统负责更新实况窗,完全不需要你的App醒来。

关键就在服务端发的这条消息。我们看看一个"更新取餐状态"的推送请求大概长啥样:

// 这是你的后台服务(比如Node.js)需要构造和发送的请求
const https = require('https');

const updateLiveViewViaPush = async (projectId, accessToken, pushToken, orderId, newStatus) => {
  const data = JSON.stringify({
    payload: {
      activityId: orderId, // 必须和本地创建的 liveView.id 一致!
      operation: 1, // 1代表更新,0创建,2结束
      event: 'PICK_UP',
      status: newStatus, // 新状态,如'READY_FOR_PICKUP'
      version: Date.now(), // 版本号,确保更新有序,后发的要比先发的大
      activityData: {
        notificationData: {
          type: 4, // LAYOUT_TYPE_PICKUP 在推送API中对应值,查文档确认
          contentTitle: '餐品已备好', // 可以更新标题
          contentText: [{ text: `取餐码已更新为 ${newCode}` }], // 更新内容
          // ... 其他可以更新的字段,参考推送实况窗消息文档
        }
      },
      token: [pushToken] // 推送给哪个设备
    }
  });

  const options = {
    hostname: 'push-api.cloud.huawei.com',
    path: `/v3/${projectId}/messages:send`,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${accessToken}`,
      'push-type': '7' // 固定值7,代表实况窗消息
    }
  };

  const req = https.request(options, (res) => {
    // 处理响应...
  });
  req.write(data);
  req.end();
};

这里有几个血泪经验:

  • activityId 和 id 必须是同一个东西,不然系统对不上号。
  • version 要递增,防止旧消息覆盖新消息(网络延迟可能导致顺序错乱)。
  • push-type: 7 这个头千万别忘了,否则会被当成普通推送处理。
  • 开通权益:在AGC后台,不光要开通"实况窗服务权益",如果要用Push Kit更新,还得先开通 “推送服务权益” ,并且在项目设置里把数据处理位置设好(通常是中国)。没设对,消息根本下不去。

绕不开的坑和最佳姿势

  1. 别更太猛,有频控:系统怕你刷屏。打车和赛事场景,每小时最多更180次;其他场景每小时最多60次。超了的直接丢。
  2. 保序很重要:本地更新靠 sequence 字段,云端更新靠 version 字段。确保后发的数字比当前的大。
  3. 生命周期不是无限的:单个实况窗最多活8小时。超过4小时没任何更新,系统就直接帮你结束了。所以关键状态要及时推。
  4. 设计规范是红线:你的实况窗长得丑、乱发广告、或者用在非场景(比如当成普通促销推送),轻则审核不过,重则权益被封。老老实实按设计规范来。
  5. 联调测试:在AGC后台"调测设备管理"里添加上你开发板的设备信息,开通测试权限。先用测试消息 (testMessage: true) 把流程跑通,每天有额度限制。

最后,串起来的工作流

唠了这么多,咱们把整个最佳实践串成一张图,你照着做就行:

  1. 用户下单,App在前台。
  2. 调用 liveViewManager.startLiveView(…) 本地创建实况窗(含胶囊)。
  3. 同时,App将 { orderId, pushToken, event } 绑定,上报给业务服务器。
  4. (用户切到后台,甚至杀进程)
  5. 订单状态变化(商家接单)。
  6. 业务服务器调用 Push Kit 服务端API,发送 operation=1 的更新消息。
  7. 华为Push服务器将消息送达用户设备。
  8. 系统自动更新实况窗界面。
  9. 订单完成,业务服务器调用 Push Kit API,发送 operation=2 的结束消息。
  10. 系统移除实况窗。

说到底,技术选型没有银弹。liveViewManager 和 Push Kit 在鸿蒙实况窗生态里就是一对黄金搭档。一个管"出生"和"高频心跳",一个管"一生照料"和"善终"。理解它俩的分工,才能写出既体验流畅又稳定可靠的服务。

下次再设计实况窗,别光想着 liveViewManager.updateLiveView 那一行代码了。问问自己:“万一用户这会儿不在我App里,这消息还能不能准点到?” 想明白这个问题,你就知道该怎么做了。

希望这篇能帮你省下半夜接报警电话的功夫。如果有更奇葩的坑,欢迎来找我唠唠,一起填坑,比一个人折腾有意思多了。

Logo

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

更多推荐