本地通知是应用程序在无需远程服务器参与的情况下向用户发送的消息。所有数据直接在接收者的设备上处理和显示。这种机制适用于应用程序在后台或关闭时的提醒、定时器和闹钟。根据 Apple Developer Documentation,UNUserNotificationCenter 提供 iOS 上本地通知的集中管理。
要点
本地通知是由操作系统在安装应用的同一设备上生成和显示的触发消息。与推送通知不同,本地通知不经过外部服务器 — 所有计划逻辑都在客户端执行。
此类通知无论应用处于何种状态都能工作:活动、最小化或完全关闭。操作系统负责在指定时间传递通知,而开发者只需指定内容和触发器。
系统保证即使在没有网络的情况下也能传递本地通知。这是相对于需要稳定互联网连接和正常运行的服务器的推送通知的关键优势。
每个本地通知由三部分组成:内容(标题、正文、声音)、触发器(时间或地理条件)和请求标识符。标识符允许在通知传递之前取消或更新通知。
开发者可以为 iOS 上的一个应用设置最多 64 个本地通知,在 Android 上则数量不限。这种差异与操作系统的架构限制有关。
两个平台都提供自己的 API 来处理本地通知。在 iOS 上,核心元素是 UNUserNotificationCenter,在 Android 上则是 NotificationManager。尽管接口不同,逻辑是一样的:应用创建请求,在系统中注册,操作系统在指定时间传递通知。
iOS 使用 UNCalendarNotificationTrigger 处理日历事件,UNTimeIntervalNotificationTrigger 处理时间间隔,UNLocationNotificationTrigger 处理地理位置。Android 提供 AlarmManager、WorkManager 以及通过 setExact 进行精确计划。
从 Android 12 开始 — SCHEDULE_EXACT_ALARM 需要用户的特殊权限。在 iOS 上,权限通过 UNUserNotificationCenter.requestAuthorization 一次性请求,用户选择访问级别:横幅、声音、标记。
本地通知按触发器类型分类,而非按内容分类。每种类型决定通知何时以及在什么条件下显示给用户。
在 iOS 和 Android 上,触发器类型的实现不同,尽管逻辑分类相同。iOS 使用 UNCalendarNotificationTrigger 处理日期,UNTimeIntervalNotificationTrigger 处理时间间隔,UNLocationNotificationTrigger 处理地理位置。Android 提供带有 setExact 和 setRepeating 的 AlarmManager,以及用于延迟任务的 WorkManager。
| 触发器类型 | 描述 | 示例 |
|---|---|---|
| 时间间隔 | 启动后 N 秒的通知 | 倒计时定时器 |
| 日历日期 | 在特定时间和日期的通知 | 会议提醒 |
| 地理位置 | 进入/离开区域时的通知 | 商店附近的提醒 |
| 无条件 | 调用 API 时立即传递 | 下载通知 |
在 iOS 上还提供 UNNotificationAttachment — 在通知正文中附加图片、音频或视频。Android 通过 NotificationCompat.Style 支持带有按钮和大图片的自定义模板。
触发器的选择取决于场景:日历提醒通过日历触发器效果最佳,地理提醒则通过地理位置。时间间隔触发器适用于具有固定周期的定期事件。
在 iOS 上,即使应用关闭,本地通知也会由系统传递 — UNUserNotificationCenter 独立管理队列。在 Android 上,传递取决于所选的机制:AlarmManager 即使在屏幕关闭时也会触发,而 WorkManager 则考虑节能。
本地通知解决外部基础设施冗余或不可用的任务。主要场景:提醒、定时器、教学提示和延迟操作。
Localytics 的研究表明,使用本地提醒的应用在安装后第一周保留的用户多 35%。这使本地通知成为强大的入职工具。
重要的是不要滥用频率 — 系统自动对来自同一应用的通知进行分组,用户可以在通知烦人时关闭所有本地通知。对于非关键事件,最佳频率为每天不超过 2–3 条通知。
要在 Android 上计划本地通知,请使用 NotificationManager 配合 AlarmManager。从 Android 8+ 开始,必须首先创建通知渠道,否则通知将不会显示。
val channelId = "reminder_channel"
val notificationId = "task_reminder_42"
val channel = NotificationChannel(
channelId,
"提醒",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "任务提醒频道"
}
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(channel)
val intent = Intent(this, ReminderReceiver::class.java).apply {
putExtra("notification_id", notificationId)
putExtra("channel_id", channelId)
}
val pendingIntent = PendingIntent.getBroadcast(
this, notificationId.hashCode(),
intent, PendingIntent.FLAG_UPDATE_CURRENT
)
val alarmManager = getSystemService(AlarmManager::class.java)
alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
triggerTimeMillis,
pendingIntent
)
在 Android 12+ 上,在调用 setExact 之前检查 SCHEDULE_EXACT_ALARM 权限。如果未授予权限 — 请使用 setWindow,它保证在时间窗口内传递。
当 AlarmManager 触发时,系统发送一个由 BroadcastReceiver 接收的广播 Intent。在其内部需要通过 NotificationManager 创建和显示通知。确保 PendingIntent 使用 FLAG_UPDATE_CURRENT,否则在数据更改时,旧通知将继续使用过时的 Intent。
在 iOS 上,通过 UNUserNotificationCenter 使用 UNMutableNotificationContent 和其中一个触发器创建本地通知。在计划之前需要请求用户权限。
import UserNotifications
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
guard granted else { return }
}
let content = UNMutableNotificationContent()
content.title = "任务提醒"
content.body = "别忘了在 18:00 前完成报告"
content.sound = .default
content.userInfo = ["task_id": "42"]
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: 3600,
repeats: false
)
let request = UNNotificationRequest(
identifier: "task_reminder_42",
content: content,
trigger: trigger
)
center.add(request)
iOS 支持最多 64 个同时进行的本地通知请求。如果超过限制 — 系统会拒绝新请求,直到活动的请求被传递或取消。使用 getPendingNotificationRequests 检查当前队列。
当用户与 iOS 本地通知交互时,会调用 UNUserNotificationCenterDelegate 代理的 userNotificationCenter:didReceive response 方法。在此方法中,可以获取请求标识符、actionIdentifier(按下了哪个按钮)和自定义 userInfo。这可以区分简单打开通知和按下特定操作按钮。
为了使本地通知有用且不打扰用户,请遵循几个关键规则。第一:控制频率 — 非关键事件每天不超过 2–3 条通知,否则用户将关闭应用的所有通知。
第二:给用户选择权。在界面中添加关闭特定类型本地通知的功能。在 Android 上,建议使用单独的具有低重要性的 NotificationChannel,在 iOS 上 — 在应用设置中使用单独的类别。
第三:上下文相关性 — 通知应在用户需要时出现。地理触发器非常适合提醒家附近的事务,日历触发器 — 用于会议,间隔触发器 — 用于喝水或伸展等常规活动。不要不必要地混合类型。
第四:在真实设备上测试。iOS 模拟器不会模拟本地通知传递的所有场景,尤其是在后台。在 Android 上使用 adb shell dumpsys notification 检查计划通知队列及其参数。第五:始终让用户通过应用界面关闭通知 — 这是 UX 和 App Store Review Guidelines 的强制性要求。
常见问题
本地通知由设备在无需服务器参与的情况下计划和传递。推送通知需要外部服务(FCM、APNS)和互联网连接。本地通知可以离线工作,推送 — 仅在有网络访问时。
iOS 限制同时计划64个请求。Android 没有严格限制,但超过 500 条通知可能会降低系统性能并影响传递时间。
可以,在 iOS 上使用带有请求标识符的 removePendingNotificationRequests。在 Android 上调用 NotificationManager.cancel 或通过 AlarmManager 取消 PendingIntent。取消时必须使用唯一标识符。
在 iOS 上,权限必须通过 requestAuthorization 请求。在 Android 13+(Tiramisu)上,也需要 POST_NOTIFICATIONS 权限。旧版本的 Android 不需要显式权限即可使用本地通知。
在 iOS 上创建 UNNotificationAction 并添加到 UNNotificationCategory。在 Android 上使用带有 PendingIntent 的 NotificationCompat.Builder.addAction 给 BroadcastReceiver。每个按钮触发应用中的单独操作。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。