前言
随着鸿蒙生态的快速发展,越来越多的开发者开始关注如何快速将现有应用适配到鸿蒙平台。UniApp作为一个成熟的跨平台开发框架,已经支持了鸿蒙应用的开发。然而在实际开发过程中,我们会遇到一些平台特有的问题和挑战。本文将基于一个服药提醒应用的开发实践,详细介绍如何解决鸿蒙平台的底部安全区适配问题,以及如何使用UTS插件实现代理提醒功能。
一、底部小白条问题的解决方案(SafeArea适配)
1.1 问题背景
在鸿蒙设备上,特别是全面屏设备,底部通常会有一个用于手势导航的"小白条"区域。这个区域被称为安全区域(Safe Area),如果应用界面没有正确处理这个区域,会导致底部内容被遮挡,影响用户体验。
1.2 SafeArea配置方案
UniApp为鸿蒙平台提供了完善的安全区域配置方案。我们需要在manifest.json文件中进行配置:
{
"app-harmony": {
"safearea": {
"background": "#ffffff",
"backgroundDark": "#2f0508",
"bottom": {
"offset": "none"
}
}
}
}
配置说明:
- background: 浅色模式下安全区域的背景色,这里设置为白色
#ffffff,与应用主题保持一致 - backgroundDark: 深色模式下安全区域的背景色,设置为深红色
#2f0508,适配暗黑主题 - bottom.offset: 底部区域占位策略,设置为
"none"表示在没有TabBar时不需要占位
我们成功解决了底部小白条的适配问题,确保应用在各种鸿蒙设备上都能正常显示和交互。
二、代理提醒功能的UTS插件实现
2.1 代理提醒简介
代理提醒(Reminder Agent)是鸿蒙系统提供的一项重要能力,允许应用在后台设置定时提醒,即使应用被关闭也能准时触发。这对于服药提醒、日程管理等场景至关重要。
鸿蒙系统的代理提醒支持三种类型:
- 闹钟提醒(ALARM): 基于时间的周期性提醒,适合每天固定时间的场景
- 日历提醒(CALENDAR): 基于日期的事件提醒,适合特定日期的事件
- 倒计时提醒(TIMER): 基于时长的一次性提醒,适合短期计时场景
2.2 为什么需要UTS插件
UniApp虽然支持鸿蒙平台,但并未内置代理提醒的API。要使用鸿蒙的原生能力,我们需要通过UTS(UniApp TypeScript)插件来调用鸿蒙的原生API。
UTS插件的优势:
- 类型安全: 基于TypeScript,提供完整的类型检查
- 性能优秀: 编译为原生代码,性能接近原生开发
- 开发便捷: 语法接近TypeScript,学习成本低
- 跨平台: 可以为不同平台编写不同的实现
2.3 UTS插件项目结构
我们创建了一个名为uni-reminder的UTS插件,项目结构如下:
uni_modules/
└── uni-reminder/
├── utssdk/
│ ├── app-harmony/ # 鸿蒙平台实现
│ │ └── index.uts # 主要实现文件
│ └── interface.uts # 接口定义文件
├── package.json
└── readme.md
关键文件说明:
- interface.uts: 定义插件的接口类型,包括所有方法的参数和回调类型
- app-harmony/index.uts: 鸿蒙平台的具体实现,调用鸿蒙原生API
2.4 接口定义(interface.uts)
首先,我们需要定义清晰的接口类型。以闹钟提醒为例:
// 闹钟提醒选项
export type PublishAlarmReminderOptions = {
hour: number // 闹钟小时数(0-23)
minute: number // 闹钟分钟数(0-59)
daysOfWeek?: Array<number> | null // 重复日期,周日=0
title?: string | null // 提醒标题
content?: string | null // 提醒内容
success?: PublishAlarmReminderSuccessCallback | null
fail?: PublishAlarmReminderFailCallback | null
complete?: PublishAlarmReminderCompleteCallback | null
}
// 成功回调返回值
export type PublishAlarmReminderSuccess = {
errMsg: string
reminderId: number // 提醒ID,用于后续取消
}
// 定义uni对象上的方法
export interface Uni {
publishAlarmReminder(options: PublishAlarmReminderOptions): void
publishCalendarReminder(options: PublishCalendarReminderOptions): void
publishTimerReminder(options: PublishTimerReminderOptions): void
cancelReminder(options: CancelReminderOptions): void
cancelAllReminders(options: CancelAllRemindersOptions): void
getValidReminders(options: GetValidRemindersOptions): void
}
这种接口定义方式遵循了UniApp的API设计规范,使用success/fail/complete回调模式,保持了与UniApp其他API的一致性。
2.5 鸿蒙平台核心实现
2.5.1 导入鸿蒙原生API
import reminderAgentManager from '@ohos.reminderAgentManager'
import { BusinessError } from '@kit.BasicServicesKit'
- reminderAgentManager: 鸿蒙的代理提醒管理器
- BusinessError: 鸿蒙的业务错误类型
2.5.2 实现闹钟提醒
export function publishAlarmReminder(options: PublishAlarmReminderOptions): void {
try {
// 构建闹钟提醒请求对象
const requestObj = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_ALARM,
hour: options.hour,
minute: options.minute,
title: options.title || '闹钟提醒',
content: options.content || '该起床了',
wantAgent: {
pkgName: 'com.liudd1.anshichiyao',
abilityName: 'EntryAbility'
},
actionButton: [{
title: '关闭',
type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_CLOSE
}]
} as UTSJSONObject
// 设置重复日期
if (options.daysOfWeek && options.daysOfWeek.length > 0) {
requestObj['daysOfWeek'] = options.daysOfWeek
}
// 发布提醒
reminderAgentManager.publishReminder(
requestObj as unknown as reminderAgentManager.ReminderRequest
)
.then((reminderId: number) => {
options?.success?.({
errMsg: 'publishAlarmReminder:ok',
reminderId: reminderId
})
options?.complete?.({ errMsg: 'publishAlarmReminder:ok' })
})
.catch((err: BusinessError) => {
options?.fail?.({ errMsg: `publishAlarmReminder:fail ${err.message}` })
options?.complete?.({ errMsg: `publishAlarmReminder:fail ${err.message}` })
})
} catch (err) {
const error = err as BusinessError
options?.fail?.({ errMsg: `publishAlarmReminder:fail ${error.message}` })
options?.complete?.({ errMsg: `publishAlarmReminder:fail ${error.message}` })
}
}
实现要点:
- reminderType: 指定提醒类型为闹钟
- wantAgent: 配置点击提醒后要启动的应用和页面
- actionButton: 添加操作按钮
- daysOfWeek: 可选的重复日期数组
- 错误处理: 使用try-catch和Promise.catch双重错误处理
2.5.3 实现日历提醒
export function publishCalendarReminder(options: PublishCalendarReminderOptions): void {
try {
const date = new Date(options.dateTime)
const requestObj = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_CALENDAR,
dateTime: {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hour: date.getHours(),
minute: date.getMinutes()
},
title: options.title || '日历提醒',
content: options.content || '事件提醒',
wantAgent: {
pkgName: 'com.liudd1.anshichiyao',
abilityName: 'EntryAbility'
},
actionButton: [{
title: '关闭',
type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_CLOSE
}]
} as UTSJSONObject
// 设置重复月份和日期
if (options.repeatMonths && options.repeatMonths.length > 0) {
requestObj['repeatMonths'] = options.repeatMonths
}
if (options.repeatDays && options.repeatDays.length > 0) {
requestObj['repeatDays'] = options.repeatDays
}
// 发布提醒(Promise处理逻辑同闹钟提醒)
// ...
} catch (err) {
// 错误处理
}
}
2.5.4 实现倒计时提醒
export function publishTimerReminder(options: PublishTimerReminderOptions): void {
try {
const requestObj = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER,
triggerTimeInSeconds: options.triggerTimeInSeconds,
title: options.title || '倒计时提醒',
content: options.content || '倒计时已到',
wantAgent: {
pkgName: 'com.liudd1.anshichiyao',
abilityName: 'EntryAbility'
},
actionButton: [{
title: '关闭',
type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_CLOSE
}]
} as UTSJSONObject
// 发布提醒
// ...
} catch (err) {
// 错误处理
}
}
2.6 权限配置
使用代理提醒功能需要在manifest.json中声明权限:
{
"app-harmony": {
"permissions": [
"ohos.permission.PUBLISH_AGENT_REMINDER"
]
}
}
同时配合通知权限插件请求用户授权:
uni.requestNotification({
success: (res) => {
if (res.granted) {
console.log('通知权限已获取')
}
}
})
2.7 在应用中使用插件
插件开发完成后,在应用中的使用非常简单:
export default {
data() {
return {
reminderIds: []
}
},
methods: {
// 设置每天早上8点的服药提醒
setMedicineReminder() {
uni.publishAlarmReminder({
hour: 8,
minute: 0,
daysOfWeek: [1, 2, 3, 4, 5, 6, 0],
title: '服药提醒',
content: '该吃药了,记得按时服药哦!',
success: (res) => {
console.log('提醒设置成功,ID:', res.reminderId)
this.reminderIds.push(res.reminderId)
uni.showToast({ title: '提醒设置成功' })
},
fail: (err) => {
console.error('提醒设置失败:', err.errMsg)
}
})
},
// 设置30分钟后的倒计时提醒
setTimerReminder() {
uni.publishTimerReminder({
triggerTimeInSeconds: 30 * 60,
title: '服药倒计时',
content: '30分钟已到,该服药了',
success: (res) => {
this.reminderIds.push(res.reminderId)
}
})
},
// 取消所有提醒
cancelAllReminders() {
uni.cancelAllReminders({
success: () => {
this.reminderIds = []
uni.showToast({ title: '已清空所有提醒' })
}
})
}
}
}
希望本文的分享能够帮助到正在开发鸿蒙应用的开发者们,让我们一起为鸿蒙生态的繁荣贡献力量!