HarmonyOS开发:推送服务——Push Kit集成

举报
Jack20 发表于 2026/06/25 20:53:47 2026/06/25
【摘要】 HarmonyOS开发:推送服务——Push Kit集成📌 核心要点:Push Kit是应用触达用户的"扩音器"——集成推送服务、发送通知消息、处理推送点击、分析推送效果,让关键信息在合适的时间送达合适的用户。 背景与动机你的应用有一个限时促销活动,怎么通知用户?等用户自己打开应用?那黄花菜都凉了。你的应用发布了一个重要更新修复了崩溃Bug,怎么通知受影响的用户?在更新日志里写?用户根本...

HarmonyOS开发:推送服务——Push Kit集成

📌 核心要点:Push Kit是应用触达用户的"扩音器"——集成推送服务、发送通知消息、处理推送点击、分析推送效果,让关键信息在合适的时间送达合适的用户。

背景与动机

你的应用有一个限时促销活动,怎么通知用户?等用户自己打开应用?那黄花菜都凉了。

你的应用发布了一个重要更新修复了崩溃Bug,怎么通知受影响的用户?在更新日志里写?用户根本不看。

推送通知是应用触达用户最直接的方式——即使用户没有打开应用,推送消息也能出现在通知栏里,提醒用户"嘿,有重要的事情"。

但推送也是一把双刃剑——推得好,用户回访率提升30%;推得差,用户直接关掉通知权限甚至卸载应用。

推送服务要解决的核心问题:

  • 怎么推:集成Push Kit,获取推送令牌,建立推送通道
  • 推什么:通知消息、静默消息、富媒体消息,不同场景用不同类型
  • 怎么处理:用户点击推送后的跳转逻辑,别点了没反应
  • 推得怎样:推送到达率、点击率、转化率,用数据优化推送策略

鸿蒙的Push Kit提供了一套完整的推送解决方案,从令牌管理到消息处理,覆盖了推送的全链路。

核心原理

Push Kit推送的完整链路:应用获取Token → 服务端存储Token → 服务端推送消息 → Push Kit投递 → 用户点击 → 应用处理

flowchart LR
    subgraph 客户端
        A[应用启动] --> B[请求Push Token]
        B --> C[上报Token到服务端]
    end
    
    subgraph 服务端
        C --> D[存储Token]
        D --> E[触发推送<br/>运营/业务/系统]
        E --> F[调用Push Kit API]
    end
    
    subgraph Push Kit
        F --> G[消息路由]
        G --> H[在线推送<br/>实时到达]
        G --> I[离线推送<br/>等设备上线]
    end
    
    subgraph 用户端
        H --> J[通知栏展示]
        I --> J
        J --> K{用户操作}
        K -->|点击| L[打开应用<br/>跳转指定页面]
        K -->|忽略| M[通知消失]
        K -->|关闭| N[关闭通知权限]
    end
    
    classDef client fill:#6C5CE7,stroke:#5B4BC9,color:#fff
    classDef server fill:#00B894,stroke:#00A383,color:#fff
    classDef pushkit fill:#FDCB6E,stroke:#F0B429,color:#333
    classDef user fill:#FF7675,stroke:#D63031,color:#fff
    classDef decision fill:#74B9FF,stroke:#0984E3,color:#fff
    
    class A,B,C client
    class D,E,F server
    class G,H,I pushkit
    class J user
    class K decision
    class L,M,N user

推送消息类型:

消息类型 说明 展示方式 适用场景
通知消息 系统通知栏展示 通知栏+横幅+响铃 促销活动、系统公告
静默消息 不展示通知 无感知 数据同步、配置更新
富媒体消息 包含图片/按钮 通知栏+大图+操作按钮 商品推荐、社交互动
本地通知 本地触发 通知栏 提醒、定时任务

代码实战

基础用法:Push Kit集成与Token获取

先把Push Kit接进来,获取推送令牌。

// PushManager.ets - 推送管理器
import { pushService } from '@kit.PushKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { analytics } from '@kit.AnalyticsKit';

// 推送令牌回调
export interface PushTokenCallback {
  onTokenSuccess: (token: string) => void;
  onTokenFailure: (error: BusinessError) => void;
}

export class PushManager {
  private static instance: PushManager;
  private context: common.UIAbilityContext | null = null;
  private pushToken: string = '';
  private tokenCallback: PushTokenCallback | null = null;
  
  static getInstance(): PushManager {
    if (!PushManager.instance) {
      PushManager.instance = new PushManager();
    }
    return PushManager.instance;
  }
  
  // 初始化Push Kit
  init(context: common.UIAbilityContext, callback?: PushTokenCallback): void {
    this.context = context;
    this.tokenCallback = callback || null;
    
    try {
      // 请求推送令牌
      pushService.getToken(context).then((token: string) => {
        this.pushToken = token;
        console.info(`[Push] 获取Token成功: ${token.substring(0, 10)}...`);
        
        // 上报Token到服务端
        this.reportTokenToServer(token);
        
        // 回调
        if (this.tokenCallback) {
          this.tokenCallback.onTokenSuccess(token);
        }
      }).catch((error: BusinessError) => {
        console.error(`[Push] 获取Token失败: ${error.code} - ${error.message}`);
        
        if (this.tokenCallback) {
          this.tokenCallback.onTokenFailure(error);
        }
      });
      
      console.info('[Push] Push Kit初始化完成');
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[Push] 初始化失败: ${err.code} - ${err.message}`);
    }
  }
  
  // 获取当前Token
  getToken(): string {
    return this.pushToken;
  }
  
  // 上报Token到服务端
  private async reportTokenToServer(token: string): Promise<void> {
    // 实际项目中用HTTP请求上报到自己的推送服务
    console.info('[Push] Token已上报到服务端');
    
    // 上报Token事件
    analytics.reportEvent('push_token_obtained', {
      'token_prefix': token.substring(0, 10),
      'timestamp': Date.now(),
    });
  }
  
  // 检查通知权限
  async checkNotificationPermission(): Promise<boolean> {
    // 检查通知权限是否开启
    // 实际项目中使用notificationManager.isNotificationEnabled()
    return true;
  }
  
  // 请求通知权限
  async requestNotificationPermission(): Promise<boolean> {
    // 请求通知权限
    // 实际项目中使用notificationManager.requestNotificationEnabled()
    console.info('[Push] 请求通知权限');
    return true;
  }
  
  // 删除Token(用户关闭推送时调用)
  async deleteToken(): Promise<void> {
    if (!this.context) return;
    
    try {
      await pushService.deleteToken(this.context);
      this.pushToken = '';
      console.info('[Push] Token已删除');
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[Push] 删除Token失败: ${err.code}`);
    }
  }
}

在EntryAbility中初始化:

// EntryAbility.ets
import { PushManager } from '../push/PushManager';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam): void {
    // 初始化Push Kit
    PushManager.getInstance().init(this.context, {
      onTokenSuccess: (token: string) => {
        console.info(`Push Token: ${token}`);
      },
      onTokenFailure: (error: BusinessError) => {
        console.error(`Push Token获取失败: ${error.code}`);
      },
    });
  }
}

进阶用法:推送消息处理

推送消息来了,怎么处理?用户点击了推送,怎么跳转?

// PushMessageHandler.ets - 推送消息处理
import { AbilityConstant, Want } from '@kit.AbilityKit';
import { analytics } from '@kit.AnalyticsKit';
import { router } from '@kit.ArkUI';

// 推送消息类型
export enum PushMessageType {
  PROMOTION = 'promotion',       // 促销活动
  ORDER_UPDATE = 'order_update', // 订单更新
  SYSTEM_NOTICE = 'system',      // 系统通知
  SOCIAL = 'social',             // 社交互动
  REMINDER = 'reminder',         // 提醒
}

// 推送消息数据
export interface PushMessageData {
  messageId: string;
  type: PushMessageType;
  title: string;
  content: string;
  targetPage?: string;         // 点击后跳转的页面
  targetParams?: Record<string, string>;  // 跳转参数
  imageUrl?: string;           // 富媒体图片
  actionButtons?: PushActionButton[];  // 操作按钮
}

// 操作按钮
export interface PushActionButton {
  title: string;
  action: string;
}

// 推送效果数据
export interface PushEffectData {
  messageId: string;
  received: boolean;         // 是否收到
  clicked: boolean;          // 是否点击
  clickTime?: number;        // 点击时间
  converted: boolean;        // 是否转化
  convertTime?: number;      // 转化时间
}

export class PushMessageHandler {
  private static instance: PushMessageHandler;
  private effectData: Map<string, PushEffectData> = new Map();
  
  static getInstance(): PushMessageHandler {
    if (!PushMessageHandler.instance) {
      PushMessageHandler.instance = new PushMessageHandler();
    }
    return PushMessageHandler.instance;
  }
  
  // 处理推送消息——在Ability的onCreate/onNewWant中调用
  handleMessage(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (!want || !want.parameters) return;
    
    // 解析推送数据
    const pushData = this.parsePushData(want);
    if (!pushData) return;
    
    console.info(`[PushHandler] 收到推送: ${pushData.title}`);
    
    // 记录推送效果
    this.recordReceived(pushData.messageId);
    
    // 判断是否是点击推送打开的应用
    if (launchParam.launchReason === AbilityConstant.LaunchReason.PUSH) {
      this.handlePushClick(pushData);
    }
  }
  
  // 解析推送数据
  private parsePushData(want: Want): PushMessageData | null {
    try {
      const params = want.parameters as Record<string, string>;
      
      return {
        messageId: params['msg_id'] || '',
        type: params['msg_type'] as PushMessageType || PushMessageType.SYSTEM_NOTICE,
        title: params['title'] || '',
        content: params['content'] || '',
        targetPage: params['target_page'],
        targetParams: params['target_params'] 
          ? JSON.parse(params['target_params']) 
          : undefined,
        imageUrl: params['image_url'],
      };
    } catch (error) {
      console.error('[PushHandler] 解析推送数据失败');
      return null;
    }
  }
  
  // 处理推送点击
  private handlePushClick(pushData: PushMessageData): void {
    // 记录点击
    this.recordClicked(pushData.messageId);
    
    // 上报推送点击事件
    analytics.reportEvent('push_clicked', {
      'message_id': pushData.messageId,
      'message_type': pushData.type,
      'target_page': pushData.targetPage || 'none',
    });
    
    // 跳转到目标页面
    if (pushData.targetPage) {
      this.navigateToTarget(pushData);
    }
  }
  
  // 跳转到目标页面
  private navigateToTarget(pushData: PushMessageData): void {
    const targetMap: Record<string, string> = {
      'home': 'pages/HomePage',
      'product_detail': 'pages/ProductDetailPage',
      'order_detail': 'pages/OrderDetailPage',
      'promotion': 'pages/PromotionPage',
      'message': 'pages/MessagePage',
    };
    
    const targetUrl = targetMap[pushData.targetPage!] || 'pages/HomePage';
    
    try {
      router.pushUrl({
        url: targetUrl,
        params: pushData.targetParams || {},
      });
      
      console.info(`[PushHandler] 跳转到: ${targetUrl}`);
    } catch (error) {
      console.error(`[PushHandler] 跳转失败: ${targetUrl}`);
    }
  }
  
  // 记录推送收到
  private recordReceived(messageId: string): void {
    this.effectData.set(messageId, {
      messageId: messageId,
      received: true,
      clicked: false,
      converted: false,
    });
  }
  
  // 记录推送点击
  private recordClicked(messageId: string): void {
    const data = this.effectData.get(messageId);
    if (data) {
      data.clicked = true;
      data.clickTime = Date.now();
    }
  }
  
  // 记录推送转化(用户完成目标行为)
  recordConversion(messageId: string): void {
    const data = this.effectData.get(messageId);
    if (data) {
      data.converted = true;
      data.convertTime = Date.now();
    }
  }
  
  // 获取推送效果数据
  getEffectData(messageId: string): PushEffectData | undefined {
    return this.effectData.get(messageId);
  }
}

在Ability中处理推送:

// EntryAbility.ets
import { PushMessageHandler } from '../push/PushMessageHandler';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam): void {
    // 处理推送消息
    PushMessageHandler.getInstance().handleMessage(want, launchParam);
  }
  
  onNewWant(want, launchParam): void {
    // 应用已在后台,通过推送重新打开
    PushMessageHandler.getInstance().handleMessage(want, launchParam);
  }
}

完整示例:推送效果分析

推送发出去,到达率多少?点击率多少?转化率多少?这些数据直接影响推送策略的优化。

// PushAnalytics.ets - 推送效果分析
import { analytics } from '@kit.AnalyticsKit';
import { preferences } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { PushEffectData } from './PushMessageHandler';

// 推送效果统计
export interface PushEffectStats {
  totalSent: number;          // 总发送数
  totalReceived: number;      // 总到达数
  totalClicked: number;       // 总点击数
  totalConverted: number;     // 总转化数
  deliveryRate: number;       // 到达率
  clickRate: number;          // 点击率
  conversionRate: number;     // 转化率
  clickToConvertRate: number; // 点击到转化率
}

// 推送策略
export interface PushStrategy {
  name: string;
  description: string;
  targetSegment: string;       // 目标用户群
  pushTime: string;            // 推送时间
  frequency: string;           // 推送频率
  messageTypes: string[];      // 消息类型
}

export class PushAnalytics {
  private static instance: PushAnalytics;
  private pref: preferences.Preferences | null = null;
  
  // 推送效果数据
  private effectRecords: PushEffectData[] = [];
  
  static getInstance(): PushAnalytics {
    if (!PushAnalytics.instance) {
      PushAnalytics.instance = new PushAnalytics();
    }
    return PushAnalytics.instance;
  }
  
  async init(context: common.UIAbilityContext): Promise<void> {
    this.pref = await preferences.getPreferences(context, 'push_analytics');
  }
  
  // 添加效果记录
  addEffectRecord(record: PushEffectData): void {
    this.effectRecords.push(record);
  }
  
  // 计算推送效果统计
  calculateStats(): PushEffectStats {
    const totalSent = this.effectRecords.length;
    const totalReceived = this.effectRecords.filter(r => r.received).length;
    const totalClicked = this.effectRecords.filter(r => r.clicked).length;
    const totalConverted = this.effectRecords.filter(r => r.converted).length;
    
    const deliveryRate = totalSent > 0 ? totalReceived / totalSent : 0;
    const clickRate = totalReceived > 0 ? totalClicked / totalReceived : 0;
    const conversionRate = totalReceived > 0 ? totalConverted / totalReceived : 0;
    const clickToConvertRate = totalClicked > 0 ? totalConverted / totalClicked : 0;
    
    return {
      totalSent,
      totalReceived,
      totalClicked,
      totalConverted,
      deliveryRate: Math.round(deliveryRate * 10000) / 100,
      clickRate: Math.round(clickRate * 10000) / 100,
      conversionRate: Math.round(conversionRate * 10000) / 100,
      clickToConvertRate: Math.round(clickToConvertRate * 10000) / 100,
    };
  }
  
  // 生成推送优化建议
  generateOptimizationSuggestions(stats: PushEffectStats): string[] {
    const suggestions: string[] = [];
    
    // 到达率低
    if (stats.deliveryRate < 90) {
      suggestions.push(
        `到达率${stats.deliveryRate}%偏低,建议:` +
        '1.检查Token是否有效 2.确认通知权限开启率 3.优化推送通道选择'
      );
    }
    
    // 点击率低
    if (stats.clickRate < 5) {
      suggestions.push(
        `点击率${stats.clickRate}%偏低,建议:` +
        '1.优化推送标题(更吸引人) 2.优化推送时间(用户活跃时段) ' +
        '3.个性化推送内容(基于用户偏好)'
      );
    }
    
    // 点击到转化率低
    if (stats.clickToConvertRate < 10) {
      suggestions.push(
        `点击到转化率${stats.clickToConvertRate}%偏低,建议:` +
        '1.优化落地页体验 2.缩短转化路径 3.推送内容与落地页一致'
      );
    }
    
    // 一切正常
    if (suggestions.length === 0) {
      suggestions.push('推送效果良好,建议持续监控并逐步优化。');
    }
    
    return suggestions;
  }
  
  // 获取最佳推送时间
  getBestPushTime(): { hour: number; reason: string } {
    // 基于历史数据分析最佳推送时间
    // 简化实现:返回常见最佳时间
    const timeSlots = [
      { hour: 8, reason: '早间通勤时段,用户查看手机频率高' },
      { hour: 12, reason: '午休时段,用户有更多时间浏览' },
      { hour: 19, reason: '晚间休闲时段,用户活跃度最高' },
    ];
    
    // 实际项目中应基于点击率数据选择
    return timeSlots[2]; // 默认推荐晚间
  }
  
  // 生成推送策略
  generatePushStrategy(segment: string): PushStrategy {
    const strategies: Record<string, PushStrategy> = {
      'new_user': {
        name: '新用户引导推送',
        description: '帮助新用户快速了解产品核心功能',
        targetSegment: '注册7天内的新用户',
        pushTime: '注册后2小时',
        frequency: '首周3次',
        messageTypes: ['新手引导', '功能推荐', '首单优惠'],
      },
      'active_user': {
        name: '活跃用户运营推送',
        description: '保持活跃用户粘性,提升使用频次',
        targetSegment: '7日内活跃用户',
        pushTime: '晚间19:00',
        frequency: '每周2-3次',
        messageTypes: ['个性化推荐', '活动通知', '内容更新'],
      },
      'churn_risk': {
        name: '流失风险用户召回推送',
        description: '挽回即将流失的用户',
        targetSegment: '7-14天未活跃用户',
        pushTime: '午间12:00',
        frequency: '每周1次',
        messageTypes: ['专属优惠', '新功能亮点', '好友动态'],
      },
      'churned': {
        name: '已流失用户召回推送',
        description: '尝试召回长期未活跃用户',
        targetSegment: '30天以上未活跃用户',
        pushTime: '早间8:00',
        frequency: '每月1次',
        messageTypes: ['大额优惠券', '产品重大更新', '好友邀请'],
      },
    };
    
    return strategies[segment] || strategies['active_user'];
  }
  
  // 打印推送效果报告
  printReport(): void {
    const stats = this.calculateStats();
    const suggestions = this.generateOptimizationSuggestions(stats);
    
    console.info('\n========== 推送效果报告 ==========');
    console.info(`总发送: ${stats.totalSent}`);
    console.info(`总到达: ${stats.totalReceived} (到达率${stats.deliveryRate}%)`);
    console.info(`总点击: ${stats.totalClicked} (点击率${stats.clickRate}%)`);
    console.info(`总转化: ${stats.totalConverted} (转化率${stats.conversionRate}%)`);
    console.info(`点击到转化: ${stats.clickToConvertRate}%`);
    
    console.info('\n优化建议:');
    suggestions.forEach((s, i) => {
      console.info(`  ${i + 1}. ${s}`);
    });
    
    const bestTime = this.getBestPushTime();
    console.info(`\n最佳推送时间: ${bestTime.hour}:00 (${bestTime.reason})`);
    console.info('====================================\n');
  }
}

踩坑与注意事项

1. 推送权限要引导获取

用户第一次打开应用,你直接请求推送权限,大概率被拒绝。应该先让用户感受到推送的价值,再请求权限。比如用户下单后,提示"开启通知及时了解订单状态"——这种场景下用户更愿意授权。

2. 推送频率要克制

一天推5条?用户直接关掉通知权限。推送频率建议:

  • 促销类:每天不超过1条
  • 系统通知:按需推送
  • 社交互动:按需推送
  • 内容推荐:每天不超过2条

3. 推送内容要个性化

“亲爱的用户,我们有新活动”——这种群发推送点击率极低。应该基于用户画像做个性化推送:“你收藏的商品降价了”、“你关注的主播正在直播”——这种推送点击率能翻3倍。

4. Token会失效

推送Token不是永久有效的——用户卸载重装、清除数据、系统更新都可能导致Token失效。必须定期刷新Token,并处理Token失效的情况。

5. 推送与隐私合规

推送涉及用户隐私,必须遵守相关规定:

  • 获取推送权限前告知用户用途
  • 用户可以随时关闭推送
  • 不推送敏感信息(如交易金额、密码等)
  • 保留推送记录以备审计

HarmonyOS 6适配说明

HarmonyOS 6在推送服务方面的更新:

  1. Push Kit增强:支持富媒体推送(大图、视频、操作按钮)
  2. 实时推送:新增实时消息通道,适用于IM、直播等场景
  3. 智能推送时机:AI分析用户活跃时段,自动选择最佳推送时间
  4. 推送效果分析:内置推送效果统计,到达率/点击率/转化率一目了然
// HarmonyOS 6 富媒体推送
import { notificationManager } from '@kit.NotificationKit';

// 发送富媒体通知
async function sendRichNotification(
  title: string,
  text: string,
  imageUrl: string
): Promise<void> {
  const request: notificationManager.NotificationRequest = {
    id: Date.now(),
    content: {
      notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE,
      picture: {
        title: title,
        text: text,
        expandedTitle: title,
        briefText: text,
        picture: imageUrl,  // 大图
      },
    },
    actionButtons: [
      { title: '立即查看' },
      { title: '稍后再看' },
    ],
  };
  
  await notificationManager.publish(request);
}

总结

推送服务是应用触达用户的关键通道——没有推送,用户可能永远不会回来。但推送也是一把双刃剑,推得好用户回访,推得差用户卸载。集成Push Kit获取推送通道,精心设计推送内容,分析推送效果持续优化,这是推送运营的基本功。

维度 评价
学习难度 ⭐⭐☆☆☆ 集成简单,难点在推送策略设计
使用频率 ⭐⭐⭐⭐⭐ 持续使用,每日推送
重要程度 ⭐⭐⭐⭐⭐ 直接影响用户回访率和活跃度

核心记住三点:推送频率要克制(别骚扰用户)、推送内容要个性化(别群发)、推送效果要分析(用数据优化)。推送不是目的,用户回访才是目的——如果推送不能带来有价值的回访,不如不推。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。