鸿蒙多功能工具箱开发实战(三十)-推送通知与消息处理
前言
推送通知是保持用户活跃度的重要手段。本文将讲解HarmonyOS应用的推送通知实现和消息处理机制。
一、通知基础
1.1 通知权限配置
在 module.json5 中添加权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.NOTIFICATION_CONTROLLER"
}
]
}
}
1.2 通知服务封装
import notification from '@ohos.notification'
import notificationManager from '@ohos.notificationManager'
import wantAgent from '@ohos.app.ability.wantAgent'
export class NotificationService {
private static instance: NotificationService
private constructor() {}
static getInstance(): NotificationService {
if (!NotificationService.instance) {
NotificationService.instance = new NotificationService()
}
return NotificationService.instance
}
/**
* 发送本地通知
*/
async showNotification(params: {
title: string
content: string
icon?: Resource
clickAction?: () => void
}): Promise<void> {
// 创建WantAgent用于点击跳转
let wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: 'com.example.harmonytoolbox',
abilityName: 'EntryAbility'
}
],
requestCode: 0,
operationType: wantAgent.OperationType.START_ABILITY,
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
}
const agent = await wantAgent.getWantAgent(wantAgentInfo)
// 构建通知内容
const notificationRequest: notification.NotificationRequest = {
id: Date.now(),
content: {
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: params.title,
text: params.content
}
},
wantAgent: agent
}
// 发布通知
await notificationManager.publish(notificationRequest)
}
/**
* 发送带图片的通知
*/
async showImageNotification(params: {
title: string
content: string
image: Resource
}): Promise<void> {
const notificationRequest: notification.NotificationRequest = {
id: Date.now(),
content: {
contentType: notification.ContentType.NOTIFICATION_CONTENT_PICTURE,
picture: {
title: params.title,
text: params.content,
picture: params.image
}
}
}
await notificationManager.publish(notificationRequest)
}
/**
* 取消通知
*/
async cancel(notificationId: number): Promise<void> {
await notificationManager.cancel(notificationId)
}
/**
* 取消所有通知
*/
async cancelAll(): Promise<void> {
await notificationManager.cancelAll()
}
}
二、定时通知
2.1 定时提醒
import reminderAgentManager from '@ohos.reminderAgentManager'
export class ReminderService {
/**
* 设置每日提醒
*/
static async setDailyReminder(params: {
hour: number
minute: number
title: string
content: string
}): Promise<number> {
const reminderRequest: reminderAgentManager.ReminderRequestCalendar = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_CALENDAR,
dateTime: {
year: 0, // 0表示重复
month: 0,
day: 0,
hour: params.hour,
minute: params.minute
},
repeatMonths: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
repeatDays: [1, 2, 3, 4, 5, 6, 7], // 每天重复
title: params.title,
content: params.content
}
return await reminderAgentManager.publishReminder(reminderRequest)
}
/**
* 设置一次性提醒
*/
static async setOneTimeReminder(params: {
timestamp: number
title: string
content: string
}): Promise<number> {
const date = new Date(params.timestamp)
const reminderRequest: reminderAgentManager.ReminderRequestCalendar = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_CALENDAR,
dateTime: {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hour: date.getHours(),
minute: date.getMinutes()
},
title: params.title,
content: params.content
}
return await reminderAgentManager.publishReminder(reminderRequest)
}
/**
* 取消提醒
*/
static async cancelReminder(reminderId: number): Promise<void> {
await reminderAgentManager.cancelReminder(reminderId)
}
/**
* 获取所有有效提醒
*/
static async getAllValidReminders(): Promise<Array<reminderAgentManager.ReminderRequest>> {
return await reminderAgentManager.getValidReminders()
}
}
三、推送消息处理
3.1 推送消息接收
import pushService from '@ohos.pushService'
export class PushMessageHandler {
private static instance: PushMessageHandler
static getInstance(): PushMessageHandler {
if (!PushMessageHandler.instance) {
PushMessageHandler.instance = new PushMessageHandler()
}
return PushMessageHandler.instance
}
/**
* 初始化推送
*/
async init(): Promise<void> {
// 注册推送服务
await this.registerPush()
// 监听推送消息
this.listenPushMessage()
}
/**
* 注册推送
*/
private async registerPush(): Promise<string> {
try {
const pushToken = await pushService.getToken()
console.info('Push token:', pushToken)
// 上报token到服务器
await this.reportToken(pushToken)
return pushToken
} catch (e) {
console.error('Register push failed:', e)
throw e
}
}
/**
* 监听推送消息
*/
private listenPushMessage(): void {
pushService.on('pushMessage', (message) => {
this.handlePushMessage(message)
})
}
/**
* 处理推送消息
*/
private handlePushMessage(message: any): void {
console.info('Received push message:', message)
// 解析消息内容
const { type, data } = message
switch (type) {
case 'notification':
this.showNotificationFromPush(data)
break
case 'update':
this.handleUpdateMessage(data)
break
case 'promotion':
this.handlePromotionMessage(data)
break
default:
console.warn('Unknown message type:', type)
}
}
private async showNotificationFromPush(data: any): Promise<void> {
await NotificationService.getInstance().showNotification({
title: data.title,
content: data.content
})
}
private handleUpdateMessage(data: any): void {
// 处理更新消息
}
private handlePromotionMessage(data: any): void {
// 处理推广消息
}
private async reportToken(token: string): Promise<void> {
const http = HttpService.getInstance()
await http.post('https://api.example.com/push/register', { token })
}
}
3.2 消息分类处理
export interface PushMessage {
id: string
type: 'notification' | 'update' | 'promotion' | 'system'
title: string
content: string
data?: Record<string, any>
timestamp: number
read: boolean
}
export class MessageManager {
private static messages: PushMessage[] = []
/**
* 添加消息
*/
static addMessage(message: PushMessage): void {
this.messages.unshift(message)
this.saveMessages()
}
/**
* 获取消息列表
*/
static getMessages(type?: string): PushMessage[] {
if (type) {
return this.messages.filter(m => m.type === type)
}
return this.messages
}
/**
* 获取未读数量
*/
static getUnreadCount(): number {
return this.messages.filter(m => !m.read).length
}
/**
* 标记已读
*/
static markAsRead(messageId: string): void {
const message = this.messages.find(m => m.id === messageId)
if (message) {
message.read = true
this.saveMessages()
}
}
/**
* 全部标记已读
*/
static markAllAsRead(): void {
this.messages.forEach(m => m.read = true)
this.saveMessages()
}
/**
* 删除消息
*/
static deleteMessage(messageId: string): void {
this.messages = this.messages.filter(m => m.id !== messageId)
this.saveMessages()
}
private static async saveMessages(): Promise<void> {
const prefs = await PreferencesService.getInstance()
await prefs.put('push_messages', JSON.stringify(this.messages))
}
static async loadMessages(): Promise<void> {
const prefs = await PreferencesService.getInstance()
const data = await prefs.get('push_messages', '[]') as string
this.messages = JSON.parse(data)
}
}
四、消息中心界面
@Entry
@Component
struct MessageCenterPage {
@State messages: PushMessage[] = []
@State currentType: string = 'all'
async aboutToAppear() {
await MessageManager.loadMessages()
this.messages = MessageManager.getMessages()
}
build() {
Column() {
NavBar({ title: '消息中心' })
// 类型筛选
Row() {
this.TypeTab('全部', 'all')
this.TypeTab('通知', 'notification')
this.TypeTab('系统', 'system')
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
// 消息列表
List() {
ForEach(this.getFilteredMessages(), (message: PushMessage) => {
ListItem() {
this.MessageItem(message)
}
})
}
.width('100%')
.layoutWeight(1)
}
}
@Builder
TypeTab(label: string, type: string) {
Text(label)
.fontSize(14)
.fontColor(this.currentType === type ? '#4A90E2' : '#999999')
.onClick(() => {
this.currentType = type
})
}
@Builder
MessageItem(message: PushMessage) {
Row() {
// 未读标记
if (!message.read) {
Circle()
.width(8)
.height(8)
.fill('#E74C3C')
}
Column() {
Text(message.title)
.fontSize(16)
.fontWeight(message.read ? FontWeight.Normal : FontWeight.Bold)
Text(message.content)
.fontSize(12)
.fontColor('#999999')
.margin({ top: 4 })
.maxLines(2)
Text(this.formatTime(message.timestamp))
.fontSize(10)
.fontColor('#CCCCCC')
.margin({ top: 4 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 8 })
}
.width('100%')
.padding(16)
.onClick(() => {
MessageManager.markAsRead(message.id)
this.messages = […MessageManager.getMessages()]
})
}
private getFilteredMessages(): PushMessage[] {
if (this.currentType === 'all') {
return this.messages
}
return this.messages.filter(m => m.type === this.currentType)
}
private formatTime(timestamp: number): string {
const date = new Date(timestamp)
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
}
}
五、高级推送功能
5.1 推送流程图
#mermaid-svg-R3hAtnhEbNbjwpk3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-R3hAtnhEbNbjwpk3 .error-icon{fill:#552222;}#mermaid-svg-R3hAtnhEbNbjwpk3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R3hAtnhEbNbjwpk3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .marker.cross{stroke:#333333;}#mermaid-svg-R3hAtnhEbNbjwpk3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R3hAtnhEbNbjwpk3 p{margin:0;}#mermaid-svg-R3hAtnhEbNbjwpk3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .cluster-label text{fill:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .cluster-label span{color:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .cluster-label span p{background-color:transparent;}#mermaid-svg-R3hAtnhEbNbjwpk3 .label text,#mermaid-svg-R3hAtnhEbNbjwpk3 span{fill:#333;color:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .node rect,#mermaid-svg-R3hAtnhEbNbjwpk3 .node circle,#mermaid-svg-R3hAtnhEbNbjwpk3 .node ellipse,#mermaid-svg-R3hAtnhEbNbjwpk3 .node polygon,#mermaid-svg-R3hAtnhEbNbjwpk3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .rough-node .label text,#mermaid-svg-R3hAtnhEbNbjwpk3 .node .label text,#mermaid-svg-R3hAtnhEbNbjwpk3 .image-shape .label,#mermaid-svg-R3hAtnhEbNbjwpk3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-R3hAtnhEbNbjwpk3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .rough-node .label,#mermaid-svg-R3hAtnhEbNbjwpk3 .node .label,#mermaid-svg-R3hAtnhEbNbjwpk3 .image-shape .label,#mermaid-svg-R3hAtnhEbNbjwpk3 .icon-shape .label{text-align:center;}#mermaid-svg-R3hAtnhEbNbjwpk3 .node.clickable{cursor:pointer;}#mermaid-svg-R3hAtnhEbNbjwpk3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .arrowheadPath{fill:#333333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R3hAtnhEbNbjwpk3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-R3hAtnhEbNbjwpk3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R3hAtnhEbNbjwpk3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-R3hAtnhEbNbjwpk3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .cluster text{fill:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 .cluster span{color:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-R3hAtnhEbNbjwpk3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-R3hAtnhEbNbjwpk3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-R3hAtnhEbNbjwpk3 .icon-shape,#mermaid-svg-R3hAtnhEbNbjwpk3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R3hAtnhEbNbjwpk3 .icon-shape p,#mermaid-svg-R3hAtnhEbNbjwpk3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-R3hAtnhEbNbjwpk3 .icon-shape .label rect,#mermaid-svg-R3hAtnhEbNbjwpk3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R3hAtnhEbNbjwpk3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-R3hAtnhEbNbjwpk3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-R3hAtnhEbNbjwpk3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
服务端推送
Push Kit接收
应用处理
应用在前台?
直接显示
系统通知
用户交互
5.2 富媒体通知
class RichNotification {
static async show(data: RichNotificationData) {
await notificationManager.publish({
id: data.id,
title: data.title,
text: data.text,
largeIcon: data.image, // 大图
additionalText: data.summary
})
}
}
5.3 通知渠道
// 创建通知渠道
async function createChannel() {
await notificationManager.addSlot({
type: notificationManager.SlotType.SOCIAL_COMMUNICATION,
level: notificationManager.SlotLevel.LEVEL_HIGH,
desc: '重要消息通知'
})
}
图 1 实时金价页面订阅提醒

六、小结
本文详细讲解了推送通知:
系列文章导航:
下期预告:鸿蒙多功能工具箱开发实战(三十一)-小组件开发


