HarmonyOS 6学习:手机向穿戴设备推送模板通知实现
摘要:本文详细介绍了在HarmonyOS 6分布式系统中,手机应用向穿戴设备推送模板通知的技术实现方案。基于WearEngineKit的NotifyClient接口,开发者可实现标准化、可交互的通知推送,无需穿戴设备安装对应应用。文章涵盖了开发环境配置、权限申请、核心API使用、完整实现步骤及最佳实践,包括设备连接管理、通知队列优化和错误处理策略。通过模板化设计,通知支持标题、内容及最多3个自定义
引言
随着智能穿戴设备的普及,用户对跨设备协同体验的要求越来越高。在HarmonyOS 6分布式系统中,手机应用能够主动向已连接的穿戴设备推送通知,实现了真正的设备间无缝协同。这种能力不仅限于简单的消息转发,而是通过模板化通知提供了标准化、可交互、可定制的通知体验。
根据华为官方技术文档,手机应用向穿戴设备推送模板通知的核心是通过WearEngine Kit的NotifyClient.notify()方法实现的。这个功能让开发者能够在穿戴设备上展示包含标题、内容、按钮等元素的标准化通知,即使用户的穿戴设备上没有安装对应的应用,也能够正常接收和显示这些通知。
技术背景与原理
穿戴服务架构
HarmonyOS的Wear Engine Kit(穿戴服务)是连接手机与穿戴设备的桥梁。它基于分布式软总线技术,实现了设备间的能力共享与数据同步。架构主要包括:
-
设备管理模块:负责设备发现、连接和管理
-
通信能力模块:支持文件传输和消息通知
-
传感器能力模块:提供传感器数据访问
-
意图框架:通过全局意图理解用户需求
模板通知特点
与传统通知相比,模板通知具有以下优势:
-
格式标准化:预定义模板确保在不同设备上显示一致
-
交互丰富:支持最多三个自定义按钮
-
振动可定制:提供三种重要性级别的振动模式
-
设备兼容:无需穿戴设备侧安装对应应用
工作原理
手机应用向穿戴设备推送模板通知的完整流程如下:
应用请求 → Wear Engine SDK → 分布式软总线 → 穿戴设备服务 → 设备显示
↓ ↓ ↓ ↓ ↓
生成通知 → 封装协议消息 → 跨设备传输 → 解析渲染 → 用户交互
开发环境准备
权限申请与配置
在开始开发前,需要完成以下配置:
1. 申请Wear Engine服务权限
在华为开发者联盟申请开通Wear Engine服务权限,这是使用所有穿戴相关API的前提条件。
2. 配置应用权限
在应用的module.json5文件中添加权限声明:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.NOTIFICATION_CONTROLLER",
"reason": "用于向穿戴设备发送通知"
},
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "用于跨设备数据同步"
}
]
}
}
3. 集成Wear Engine SDK
在项目的build.gradle文件中添加依赖:
dependencies {
implementation 'com.huawei.wearable:wear-engine:6.0.0.300'
}
设备兼容性
支持模板通知的设备包括:
-
HUAWEI WATCH系列(H5546、H7546、H5556、H5756、H3540、H9D20)
-
HUAWEI Band系列(HA590、HA5A0)
注意事项:
-
HUAWEI WATCH B7-536与B7-738在超长续航模式下不支持
-
应用包名长度不应超过28个字符
-
穿戴设备侧无需安装对应应用
完整实现方案
核心类与接口
主要使用的API接口包括:
// 穿戴引擎管理类
import wearEngine from '@ohos.wearEngine';
// 设备信息接口
interface DeviceInfo {
deviceId: string; // 设备唯一标识
deviceName: string; // 设备名称
deviceType: number; // 设备类型
isConnected: boolean; // 连接状态
}
// 通知选项接口
interface NotificationOptions {
templateId: number; // 模板ID
pkgName: string; // 应用包名
title: string; // 通知标题
content: string; // 通知内容
remindType?: number; // 提醒类型
button?: { // 按钮配置
button_1?: string;
button_2?: string;
button_3?: string;
};
}
实现步骤
步骤1:获取已连接设备列表
import wearEngine from '@ohos.wearEngine';
import { BusinessError } from '@ohos.base';
class WearNotificationService {
private notifyClient: wearEngine.NotifyClient | undefined;
private connectedDevices: wearEngine.DeviceInfo[] = [];
// 初始化通知客户端
async initNotifyClient(context: Context): Promise<boolean> {
try {
this.notifyClient = wearEngine.getNotifyClient(context);
console.info('通知客户端初始化成功');
return true;
} catch (err) {
const businessErr = err as BusinessError;
console.error(`初始化失败,错误码: ${businessErr.code}, 错误信息: ${businessErr.message}`);
return false;
}
}
// 获取已连接设备列表
async getConnectedDevices(): Promise<wearEngine.DeviceInfo[]> {
if (!this.notifyClient) {
throw new Error('通知客户端未初始化');
}
try {
this.connectedDevices = await this.notifyClient.getConnectedDevices();
console.info(`获取到${this.connectedDevices.length}个已连接设备`);
return this.connectedDevices;
} catch (err) {
const businessErr = err as BusinessError;
console.error(`获取设备列表失败,错误码: ${businessErr.code}, 错误信息: ${businessErr.message}`);
throw err;
}
}
}
步骤2:定义NotificationOptions配置参数
// 通知选项构建器
class NotificationOptionsBuilder {
// 构建无按钮通知
static buildBasicNotification(
title: string,
content: string,
pkgName: string = 'com.example.myapp'
): wearEngine.NotificationOptions {
return {
templateId: 50, // 无按钮模板
pkgName: pkgName,
title: this.truncateString(title, 28), // 标题最多28字节
content: this.truncateString(content, 400), // 内容最多400字节
remindType: 2 // 普通重要性模式
};
}
// 构建带按钮的通知
static buildButtonNotification(
title: string,
content: string,
buttons: string[],
pkgName: string = 'com.example.myapp'
): wearEngine.NotificationOptions {
let templateId: number;
let buttonConfig: { [key: string]: string } = {};
switch (buttons.length) {
case 1:
templateId = 51;
buttonConfig = { button_1: this.truncateString(buttons[0], 12) };
break;
case 2:
templateId = 52;
buttonConfig = {
button_1: this.truncateString(buttons[0], 12),
button_2: this.truncateString(buttons[1], 12)
};
break;
case 3:
templateId = 53;
buttonConfig = {
button_1: this.truncateString(buttons[0], 12),
button_2: this.truncateString(buttons[1], 12),
button_3: this.truncateString(buttons[2], 12)
};
break;
default:
templateId = 50;
}
return {
templateId: templateId,
pkgName: pkgName,
title: this.truncateString(title, 28),
content: this.truncateString(content, 400),
remindType: 2,
button: buttonConfig
};
}
// 字符串截断工具
private static truncateString(str: string, maxBytes: number): string {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
if (bytes.length <= maxBytes) {
return str;
}
let truncatedBytes = bytes.slice(0, maxBytes);
while (truncatedBytes[truncatedBytes.length - 1] > 127) {
truncatedBytes = truncatedBytes.slice(0, -1);
}
const decoder = new TextDecoder();
return decoder.decode(truncatedBytes);
}
}
步骤3:发送通知到穿戴设备
// 通知发送器
class NotificationSender {
private service: WearNotificationService;
constructor(service: WearNotificationService) {
this.service = service;
}
// 发送通知到指定设备
async sendNotification(
deviceId: string,
options: wearEngine.NotificationOptions
): Promise<boolean> {
if (!this.service.notifyClient) {
throw new Error('通知客户端未初始化');
}
try {
// 验证设备连接状态
const isConnected = await this.checkDeviceConnection(deviceId);
if (!isConnected) {
console.error(`设备 ${deviceId} 未连接`);
return false;
}
// 验证通知参数
const validation = this.validateNotificationOptions(options);
if (!validation.isValid) {
console.error('通知参数验证失败:', validation.errors);
return false;
}
// 发送通知
await this.service.notifyClient!.notify(deviceId, options);
console.info(`通知发送成功,设备ID: ${deviceId}`);
return true;
} catch (err) {
const businessErr = err as BusinessError;
console.error(`发送通知失败,错误码: ${businessErr.code}, 错误信息: ${businessErr.message}`);
return false;
}
}
// 验证通知参数
private validateNotificationOptions(options: wearEngine.NotificationOptions): ValidationResult {
const errors: string[] = [];
// 验证templateId
if (![50, 51, 52, 53].includes(options.templateId)) {
errors.push(`templateId必须为50、51、52或53,当前为: ${options.templateId}`);
}
// 验证包名长度
if (options.pkgName.length > 28) {
errors.push(`pkgName长度不能超过28个字符,当前长度: ${options.pkgName.length}`);
}
// 验证标题长度
const titleBytes = new TextEncoder().encode(options.title).length;
if (titleBytes > 28) {
errors.push(`标题不能超过28字节,当前字节数: ${titleBytes}`);
}
// 验证内容长度
const contentBytes = new TextEncoder().encode(options.content).length;
if (contentBytes > 400) {
errors.push(`内容不能超过400字节,当前字节数: ${contentBytes}`);
}
return {
isValid: errors.length === 0,
errors: errors
};
}
// 检查设备连接状态
private async checkDeviceConnection(deviceId: string): Promise<boolean> {
const devices = await this.service.getConnectedDevices();
return devices.some(device => device.deviceId === deviceId && device.isConnected);
}
}
步骤4:处理用户反馈
// 用户反馈处理器
class FeedbackHandler {
// 处理用户操作反馈
handleUserFeedback(feedbackData: string): void {
try {
const feedback = JSON.parse(feedbackData);
const feedbackType = feedback.feedback;
let action = '';
switch (feedbackType) {
case 0:
action = '未反馈(按HOME键退出或灭屏)';
break;
case 1:
action = '删除消息';
break;
case 2:
action = '点击按钮1';
break;
case 3:
action = '点击按钮2';
break;
case 4:
action = '点击按钮3';
break;
default:
action = '未知操作';
}
console.info(`用户操作: ${action}`);
// 这里可以根据用户操作执行相应的业务逻辑
this.executeAction(action, feedback);
} catch (err) {
console.error('解析用户反馈数据失败:', err);
}
}
// 执行对应的业务逻辑
private executeAction(action: string, feedback: any): void {
switch (action) {
case '点击按钮1':
this.handleButton1Action(feedback);
break;
case '点击按钮2':
this.handleButton2Action(feedback);
break;
case '点击按钮3':
this.handleButton3Action(feedback);
break;
case '删除消息':
this.handleDeleteAction(feedback);
break;
}
}
}
完整示例代码
下面是一个完整的示例,演示如何实现日程提醒功能:
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@ohos.base';
import wearEngine from '@ohos.wearEngine';
const TAG = 'ScheduleNotificationAbility';
const DOMAIN_NUMBER = 0xFF00;
export default class ScheduleNotificationAbility extends UIAbility {
private wearService: WearNotificationService | undefined;
private notificationSender: NotificationSender | undefined;
private feedbackHandler: FeedbackHandler | undefined;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(DOMAIN_NUMBER, TAG, 'ScheduleNotificationAbility onCreate');
// 初始化服务
this.initializeServices();
// 设置定时检查
this.setupScheduleCheck();
}
// 初始化服务
private async initializeServices(): Promise<void> {
try {
// 1. 初始化穿戴服务
this.wearService = new WearNotificationService();
const initSuccess = await this.wearService.initNotifyClient(this.context);
if (!initSuccess) {
hilog.error(DOMAIN_NUMBER, TAG, '穿戴服务初始化失败');
return;
}
// 2. 初始化其他组件
this.notificationSender = new NotificationSender(this.wearService);
this.feedbackHandler = new FeedbackHandler();
// 3. 获取已连接设备
await this.wearService.getConnectedDevices();
hilog.info(DOMAIN_NUMBER, TAG, '服务初始化成功');
} catch (err) {
const businessErr = err as BusinessError;
hilog.error(DOMAIN_NUMBER, TAG,
`服务初始化异常: code=${businessErr.code}, message=${businessErr.message}`);
}
}
// 设置日程检查
private setupScheduleCheck(): void {
// 每30分钟检查一次
setInterval(() => {
this.checkAndSendNotifications();
}, 30 * 60 * 1000);
// 立即执行一次
this.checkAndSendNotifications();
}
// 检查并发送通知
private async checkAndSendNotifications(): Promise<void> {
if (!this.notificationSender || !this.wearService) {
return;
}
try {
// 1. 获取即将开始的日程
const schedules = await this.getUpcomingSchedules();
if (schedules.length === 0) {
hilog.info(DOMAIN_NUMBER, TAG, '没有即将开始的日程');
return;
}
// 2. 获取已连接设备
const devices = await this.wearService.getConnectedDevices();
if (devices.length === 0) {
hilog.warn(DOMAIN_NUMBER, TAG, '没有找到已连接的穿戴设备');
return;
}
// 3. 为每个日程发送通知
for (const schedule of schedules) {
await this.sendScheduleNotification(schedule, devices);
}
} catch (err) {
const businessErr = err as BusinessError;
hilog.error(DOMAIN_NUMBER, TAG,
`通知发送失败: code=${businessErr.code}, message=${businessErr.message}`);
}
}
// 获取即将开始的日程
private async getUpcomingSchedules(): Promise<Schedule[]> {
// 这里应该是从数据库或网络获取数据的实际逻辑
// 返回示例数据
return [
{
id: '1',
title: '团队会议',
content: '每周项目进度同步会议',
startTime: Date.now() + 30 * 60 * 1000, // 30分钟后
buttons: ['参加', '请假']
},
{
id: '2',
title: '医生预约',
content: '年度健康检查',
startTime: Date.now() + 2 * 60 * 60 * 1000, // 2小时后
buttons: ['确认', '改期', '取消']
}
];
}
// 发送日程通知
private async sendScheduleNotification(
schedule: Schedule,
devices: wearEngine.DeviceInfo[]
): Promise<void> {
if (!this.notificationSender) {
return;
}
// 构建通知选项
const options = NotificationOptionsBuilder.buildButtonNotification(
`提醒: ${schedule.title}`,
`${schedule.content}\n时间: ${new Date(schedule.startTime).toLocaleTimeString()}`,
schedule.buttons
);
// 设置提醒类型
const minutesLeft = (schedule.startTime - Date.now()) / (60 * 1000);
if (minutesLeft <= 15) {
options.remindType = 3; // 最重要
} else if (minutesLeft <= 60) {
options.remindType = 2; // 普通
} else {
options.remindType = 1; // 次要
}
// 发送到所有设备
for (const device of devices) {
if (device.isConnected) {
const success = await this.notificationSender.sendNotification(
device.deviceId,
options
);
if (success) {
hilog.info(DOMAIN_NUMBER, TAG,
`日程通知发送成功: ${schedule.title}, 设备: ${device.deviceName}`);
} else {
hilog.warn(DOMAIN_NUMBER, TAG,
`日程通知发送失败: ${schedule.title}, 设备: ${device.deviceName}`);
}
}
}
}
onDestroy(): void {
hilog.info(DOMAIN_NUMBER, TAG, 'ScheduleNotificationAbility onDestroy');
}
}
// 类型定义
interface Schedule {
id: string;
title: string;
content: string;
startTime: number;
buttons: string[];
}
interface ValidationResult {
isValid: boolean;
errors: string[];
}
最佳实践与优化
1. 性能优化建议
连接状态管理:
// 连接状态监控
class ConnectionMonitor {
private deviceStatus: Map<string, DeviceStatus> = new Map();
// 定期检查设备状态
startMonitoring(interval: number = 30000): void {
setInterval(async () => {
await this.checkDeviceStatus();
}, interval);
}
private async checkDeviceStatus(): Promise<void> {
const devices = await wearService.getConnectedDevices();
devices.forEach(device => {
const oldStatus = this.deviceStatus.get(device.deviceId);
const newStatus: DeviceStatus = {
isConnected: device.isConnected,
lastSeen: Date.now()
};
if (oldStatus?.isConnected !== newStatus.isConnected) {
this.onStatusChange(device.deviceId, newStatus);
}
this.deviceStatus.set(device.deviceId, newStatus);
});
}
}
通知队列管理:
// 通知队列
class NotificationQueue {
private queue: NotificationTask[] = [];
private isProcessing = false;
// 添加到队列
add(task: NotificationTask): void {
this.queue.push(task);
this.processQueue();
}
// 处理队列
private async processQueue(): Promise<void> {
if (this.isProcessing || this.queue.length === 0) {
return;
}
this.isProcessing = true;
while (this.queue.length > 0) {
const task = this.queue.shift()!;
try {
await task.execute();
} catch (err) {
console.error('任务执行失败:', err);
// 重试逻辑
await this.retryTask(task);
}
// 控制发送频率
await this.delay(100);
}
this.isProcessing = false;
}
}
2. 错误处理策略
错误分类处理:
// 错误处理器
class ErrorHandler {
static handleError(error: BusinessError, context: string): void {
const errorCode = error.code;
switch (errorCode) {
case 1:
console.error(`${context}: 参数错误 - ${error.message}`);
this.handleParameterError();
break;
case 2:
console.error(`${context}: 服务不可用 - ${error.message}`);
this.handleServiceUnavailable();
break;
case 7:
console.error(`${context}: 设备未连接 - ${error.message}`);
this.handleDeviceDisconnected();
break;
default:
console.error(`${context}: 未知错误 ${errorCode} - ${error.message}`);
this.handleUnknownError(error);
}
}
// 参数错误处理
private static handleParameterError(): void {
// 验证并修正参数
}
// 服务不可用处理
private static handleServiceUnavailable(): void {
// 重试或降级处理
}
// 设备断开处理
private static handleDeviceDisconnected(): void {
// 重新连接或通知用户
}
}
3. 用户体验优化
智能提醒策略:
// 智能提醒
class SmartReminder {
// 根据用户习惯调整提醒时间
calculateRemindTime(baseTime: number, userHabits: UserHabits): number {
let remindOffset = 30 * 60 * 1000; // 默认30分钟
switch (userHabits.scheduleType) {
case 'MEETING':
remindOffset = 15 * 60 * 1000; // 会议提前15分钟
break;
case 'APPOINTMENT':
remindOffset = 60 * 60 * 1000; // 预约提前1小时
break;
case 'REMINDER':
remindOffset = 5 * 60 * 1000; // 提醒提前5分钟
break;
}
return baseTime - remindOffset;
}
// 根据设备类型调整通知
adaptForDevice(deviceType: string, notification: NotificationOptions): NotificationOptions {
const adapted = { ...notification };
if (deviceType === 'WATCH') {
// 手表屏幕小,精简内容
adapted.title = this.truncate(notification.title, 20);
adapted.content = this.truncate(notification.content, 80);
} else if (deviceType === 'BAND') {
// 手环显示空间有限
adapted.title = this.truncate(notification.title, 15);
adapted.content = '';
}
return adapted;
}
}
常见问题与解决方案
1. 通知发送失败
问题原因:
-
设备未连接
-
权限未授予
-
参数错误
-
设备不支持
解决方案:
-
检查设备连接状态
-
验证应用权限
-
检查通知参数
-
确认设备兼容性
2. 通知显示异常
问题现象:
-
内容截断
-
按钮不显示
-
格式错乱
解决方案:
-
确保标题不超过28字节
-
确保内容不超过400字节
-
按钮文本不超过12字节
-
模板ID与按钮数量匹配
3. 性能问题
优化建议:
-
批量发送通知
-
复用设备连接
-
实现智能重试
-
压缩传输数据
总结
HarmonyOS 6的穿戴服务为开发者提供了强大的跨设备通知能力。通过Wear Engine Kit的NotifyClient接口,手机应用可以主动、实时地向穿戴设备推送模板化通知。本文详细介绍了从环境准备、权限申请到完整实现的各个环节,并提供了完整的代码示例和最佳实践。
关键要点:
-
必须先申请Wear Engine服务权限
-
严格验证通知参数(长度、格式等)
-
实现完善的错误处理和重试机制
-
考虑设备兼容性和用户体验
未来展望:随着HarmonyOS生态的发展,穿戴设备与手机的协同将更加紧密。开发者可以基于此技术探索更多创新应用场景,为用户提供更好的跨设备体验。
通过本文的指导,开发者可以快速掌握HarmonyOS 6中手机向穿戴设备推送模板通知的核心技术,构建稳定、高效的跨设备通知系统。
更多推荐



所有评论(0)