前言

在移动应用开发中,通知系统是应用与用户保持连接的核心渠道。随着 HarmonyOS 6 (API 20) 的发布,通知系统已经从单纯的信息告知工具,演变为具备实时性、交互性和伴随性的服务载体。特别是实况窗(LiveView)功能的引入,彻底改变了用户获取关键信息的方式,使得服务状态可以在状态栏、锁屏界面以胶囊或卡片的形式实时呈现。

本文将深入讲解如何在 HarmonyOS 6 环境下构建一个完整的通知体系。我们将跳过基础的概念堆砌,直接进入工程实战。

你将掌握如何管理通知通道(Slot)以符合系统合规性要求,如何通过节流策略优化高频进度更新,以及如何开发全生命周期的实况窗功能。

通过本文的学习,你将能够编写出可直接用于生产环境的通知管理模块。

一、 通道管理 通知的分类与权限基石

在 HarmonyOS 的通知框架中,通知通道(NotificationSlot) 是发送通知的前置条件。系统不再允许应用随意发送未分类的通知,而是强制要求开发者根据通知的内容属性进行分类。这不仅关乎用户体验,更直接决定了通知的展示方式(是否弹窗、是否有声音、是否震动)以及在系统中的优先级。

1. Slot 类型与应用场景

NotificationManager 定义了多种 Slot 类型,我们在初始化时必须明确指定:

  • 社交通讯 (SOCIAL_COMMUNICATION):拥有最高优先级。适用于即时通讯(IM)应用的私聊、群聊消息。此类通知通常允许在屏幕顶部横幅弹出,并伴有声音和震动。
  • 服务提醒 (SERVICE_INFORMATION):拥有默认优先级。适用于即时且必要的服务状态变更,如“外卖已接单”、“打车排队中”、“快递派送中”。
  • 内容资讯 (CONTENT_INFORMATION):拥有较低优先级。适用于新闻推荐、营销活动。此类通知通常会静默展示在通知中心,不会主动打断用户。
  • 其他 (OTHER):用于不属于上述分类的场景,优先级最低。

2. 初始化通道的代码实现

通道的注册通常放在应用启动的生命周期中(如 AbilityonCreate 或页面的 aboutToAppear)。以下是封装好的通道初始化逻辑:

import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';

/**
 * 初始化通知通道
 * 建议在 EntryAbility.ets 的 onCreate 中调用
 */
async function initNotificationSlots() {
  try {
    // 定义一个服务类型的 Slot
    const serviceSlot: notificationManager.NotificationSlot = {
      // 指定 Slot 类型为服务提醒
      type: notificationManager.SlotType.SERVICE_INFORMATION, 
      // 设置优先级等级为默认
      level: notificationManager.SlotLevel.LEVEL_DEFAULT,
      // 通道描述,用户在系统设置中查看通知权限时会看到此文案
      desc: '用于业务状态变更和任务进度提醒',
      // 设置通知在锁屏上的显示模式
      lockscreenVisibility: notificationManager.LockScreenVisibility.PUBLIC,
      // 开启角标提示
      badgeFlag: true
    };

    // 向系统注册 Slot
    await notificationManager.addSlot(notificationManager.SlotType.SERVICE_INFORMATION);
    console.info('[Notification] Service slot initialized successfully');
  } catch (err) {
    const error = err as BusinessError;
    console.error(`[Notification] Failed to add slot. Code: ${error.code}, Message: ${error.message}`);
  }
}

3. 配置说明

  • type: 必须与后续发送 NotificationRequest 中的类型匹配,否则可能导致通知无法发出。
  • lockscreenVisibility: 决定了锁屏状态下通知内容的可见性。对于涉及隐私的信息(如验证码),应设置为 SECRETPRIVATE
  • badgeFlag: 决定当有未读通知时,桌面应用图标右上角是否显示红点或数字角标。

二、 基础通知 ID 管理与内容构建

发送通知的核心对象是 NotificationRequest。在实际开发中,我们需要特别注意 ID 管理幂等性 问题。

1. ID 的作用与策略

每个通知都有一个整型 ID。

  • 新增:当发送一个全新的 ID 时,系统会在通知栏创建一条新记录。
  • 更新:当使用已存在的 ID 发送通知时,系统会直接覆盖旧通知的内容,而不会产生堆叠。
  • 策略
    • 对于 IM 消息,通常希望每条消息独立显示,可以使用自增 ID 或随机 ID。
    • 对于 状态更新(如“下载中” -> “下载完成”),必须使用固定 ID,确保进度条在原地刷新。

2. 基础文本通知实现

以下是一个发送标准文本通知的封装方法:

/**
 * 发送基础文本通知
 * @param title 标题
 * @param text 内容
 */
async function publishBasicNotification(title: string, text: string) {
  // 生成一个随机 ID,用于演示多条通知并存的效果
  // 实际业务中请根据需求管理 ID
  const notificationId = Math.floor(Math.random() * 10000);

  const request: notificationManager.NotificationRequest = {
    id: notificationId,
    // 指定该通知使用的通道类型(必须已通过 addSlot 注册)
    notificationSlotType: notificationManager.SlotType.SERVICE_INFORMATION,
    content: {
      // 指定内容类型为普通文本
      notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
      normal: {
        title: title,
        text: text,
        // 附加信息,通常显示在通知的次要位置
        additionalText: '系统通知'
      }
    }
  };

  try {
    await notificationManager.publish(request);
    console.info(`[Notification] Published basic notification, ID: ${notificationId}`);
  } catch (err) {
    console.error(`[Notification] Publish failed: ${JSON.stringify(err)}`);
  }
}

三、 动态更新 进度条通知与节流策略

在文件下载、视频转码或数据同步场景中,我们需要向用户展示实时进度。HarmonyOS 提供了预置的下载模板 (DOWNLOAD_TEMPLATE),使我们无需自定义布局即可展示标准的进度条样式。

1. 性能陷阱:高频更新

开发者容易犯的一个错误是:在下载回调中每接收到一个数据包就刷新一次通知。例如,每下载 1KB 就调用一次 publish。这会导致:

  • 系统负载过高:频繁的 IPC(进程间通信)调用会占用大量 CPU。
  • UI 卡顿:通知栏渲染线程过载,导致用户下拉通知栏时出现掉帧。

2. 解决方案:节流控制

我们需要实现一个节流逻辑:仅当进度变化超过一定阈值(如 1%)或时间间隔超过一定时长(如 500ms)时,才触发系统通知更新。

3. 进度条通知封装代码

/**
 * 进度条通知管理器
 */
class ProgressNotificationManager {
  private lastUpdateTime: number = 0;
  private lastProgress: number = -1;
  // 固定 ID,保证更新的是同一条通知
  private readonly NOTIFICATION_ID = 1001;

  /**
   * 发送或更新进度
   * @param progress 当前进度 (0-100)
   * @param isDone 是否完成
   */
  async updateProgress(progress: number, isDone: boolean = false) {
    const now = Date.now();
    
    // 节流策略:
    // 1. 如果任务完成,强制更新。
    // 2. 如果距离上次更新不足 500ms 且进度变化小于 1%,则跳过。
    if (!isDone && (now - this.lastUpdateTime < 500) && (progress - this.lastProgress < 1)) {
      return;
    }

    this.lastUpdateTime = now;
    this.lastProgress = progress;

    // 构建下载模板
    const template: notificationManager.NotificationTemplate = {
      name: 'downloadTemplate', // 系统预置模板名称
      data: {
        progressValue: progress,      // 当前进度值
        progressMaxValue: 100,        // 最大值
        isProgressIndeterminate: false // false 表示确定的进度条,true 表示无限加载动画
      }
    };

    const request: notificationManager.NotificationRequest = {
      id: this.NOTIFICATION_ID,
      notificationSlotType: notificationManager.SlotType.SERVICE_INFORMATION,
      template: template, // 应用模板
      content: {
        notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: isDone ? '下载完成' : '资源下载中...',
          text: isDone ? '文件已保存' : `当前进度:${progress}%`,
          additionalText: isDone ? '' : '请保持网络连接'
        }
      }
    };

    try {
      await notificationManager.publish(request);
    } catch (err) {
      console.error(`[Notification] Progress update failed: ${JSON.stringify(err)}`);
    }
  }
}

四、 实况窗 (LiveView) 全生命周期的伴随服务

实况窗是 HarmonyOS 6 区别于传统 Android 通知系统的核心特性。它不再是一条静止的消息,而是一个具备生命周期的、实时的状态窗口。

1. 实况窗的两种形态与位置

  • 胶囊态 (Capsule):显示在屏幕左上角的状态栏区域。空间极其有限,通常只显示一个图标和简短的状态文本(如“配送中 15分”)。
  • 卡片态 (Card):用户点击胶囊或在锁屏/通知中心查看时展开。空间较大,可以展示详细信息(如地图、司机信息、当前位置)。

2. 生命周期管理

实况窗不仅是“发送”那么简单,它遵循严格的生命周期:

  1. 创建 (Create):业务开始(如用户下单成功),系统生成实况窗。
  2. 更新 (Update):业务进行中(如骑手位置变化),应用高频刷新实况窗数据。
  3. 结束 (End):业务结束(如订单送达),应用主动取消实况窗,将其转化为历史记录或销毁。

3. 数据结构

实况窗使用 NOTIFICATION_CONTENT_SYSTEM_LIVE_VIEW 内容类型。为了保证系统 UI 的统一性,开发者不需要编写 XML 布局,而是通过 extraInfo 传递特定的 JSON 数据,系统会自动渲染。

4. 封装代码

class LiveViewManager {
  private readonly LIVE_VIEW_ID = 2002;

  /**
   * 发布/更新实况窗
   * @param status 简短状态描述 (用于胶囊和标题)
   * @param detail 详细描述
   */
  async publishLiveView(status: string, detail: string) {
    const request: notificationManager.NotificationRequest = {
      id: this.LIVE_VIEW_ID,
      notificationSlotType: notificationManager.SlotType.LIVE_VIEW, // 注意:实况窗有专用 Slot 类型
      isOngoing: true, // 设置为常驻通知,用户无法左滑清除
      isAlertOnce: true, // 仅首次提醒,后续更新静默
      content: {
        notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_SYSTEM_LIVE_VIEW,
        systemLiveView: {
          title: '即时配送',
          text: status,
          typeCode: 1, // 业务类型码,不同类型对应不同的系统默认图标风格
          // 扩展参数:用于定义胶囊和卡片的具体 UI 字段
          // 注意:实际开发中需查阅 LiveView 文档匹配 key 值
          extraInfo: {
            "capsule": { 
              "status": status, 
              "icon": "capsule_icon_delivery",
              "backgroundColor": "#007DFF" 
            },
            "card": { 
              "title": "您的订单正在配送中",
              "content": detail,
              "subContent": "预计 14:30 送达"
            }
          }
        }
      }
    };

    try {
      await notificationManager.publish(request);
      console.info('[Notification] LiveView published');
    } catch (err) {
      console.error(`[Notification] LiveView failed: ${JSON.stringify(err)}`);
    }
  }

  /**
   * 结束实况窗
   */
  async stopLiveView() {
    try {
      // 取消通知即视为结束实况窗
      await notificationManager.cancel(this.LIVE_VIEW_ID);
      console.info('[Notification] LiveView stopped');
    } catch (err) {
      console.error(`[Notification] Stop failed: ${JSON.stringify(err)}`);
    }
  }
}

五、 交互性 让通知可点击 (WantAgent)

默认发送的通知是不可点击的。为了实现“点击通知跳转到应用详情页”,我们需要使用 WantAgent

1. 构建 WantAgent

WantAgent 是一个封装了意图(Want)的对象,它允许通知服务在用户点击通知时,代表应用去执行这个意图(如启动 UIAbility)。

2. 代码集成

我们需要修改 NotificationRequest 的构建逻辑,增加 wantAgent 字段。

import { wantAgent, WantAgent } from '@kit.AbilityKit';

async function createWantAgent(context: Context): Promise<WantAgent> {
  const wantInfo: wantAgent.WantAgentInfo = {
    wants: [
      {
        deviceId: '',
        bundleName: 'com.example.notificationdemo',
        abilityName: 'EntryAbility', // 跳转的目标 Ability
        action: 'ohos.want.action.home',
        entities: []
      }
    ],
    // 动作类型:启动 Ability
    operationType: wantAgent.OperationType.START_ABILITY,
    // 请求码
    requestCode: 0,
    // 标志位:如果目标 Ability 已存在,则更新它而不是重新创建
    wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
  };

  return await wantAgent.getWantAgent(wantInfo);
}

// 在发送通知时:
// request.wantAgent = await createWantAgent(context);

总结

在 HarmonyOS 6 中,构建高质量的通知体系需要关注三个维度:

  1. 规范性 (Compliance):严格遵守 Slot 通道分类,确保通知类型与系统定义匹配,这是通知能正常展示的前提。
  2. 性能 (Performance):在处理高频状态更新(如进度条)时,必须实施节流策略,避免因过度刷新导致的系统卡顿和发热。
  3. 体验 (Experience):充分利用 实况窗 (LiveView) 能力,将离散的通知消息转化为连续的服务伴随,实现信息在状态栏、锁屏、通知中心的无缝流转。
Logo

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

更多推荐