Swift 3:如何在前台启用本地通知并禁用推送通知
解决方案
针对你希望仅在前台接收本地通知,同时保持推送通知正常前后台接收的需求,我分iOS和Android两个平台给你具体的实现思路和代码示例:
iOS平台实现
iOS的通知机制中,前台收到通知会触发UNUserNotificationCenterDelegate的回调,后台则由系统直接展示通知。我们可以通过以下两步实现需求:
1. 前台通知拦截与展示控制
首先设置通知中心的代理,在前台收到通知时区分本地和推送通知,只允许本地通知展示:
import UIKit import UserNotifications @main class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 设置通知中心代理 UNUserNotificationCenter.current().delegate = self // 请求通知权限 UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in if let error = error { print("请求通知权限失败:\(error.localizedDescription)") } } return true } // 前台收到通知时的回调 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // 判断是否为本地通知(本地通知的trigger不为nil,推送通知的trigger为nil) if notification.request.trigger is UNNotificationTrigger { // 本地通知:前台展示弹窗和声音 completionHandler([.alert, .sound]) } else { // 推送通知:前台不展示(如果需要前台也展示,就改成[.alert, .sound]) completionHandler(.none) } } // 处理通知点击事件 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 根据需求处理通知点击逻辑 completionHandler() } }
2. 后台取消本地通知,前台重新调度
要让后台不展示本地通知,我们需要在应用进入后台时取消所有待触发的本地通知,回到前台时重新调度:
extension AppDelegate { // 应用进入后台时调用 func applicationDidEnterBackground(_ application: UIApplication) { // 取消所有待触发的本地通知 UNUserNotificationCenter.current().removeAllPendingNotificationRequests() // 可选:保存待触发的本地通知信息,方便前台重新调度 // 这里假设你用UserDefaults保存,实际可以根据你的通知结构调整 UNUserNotificationCenter.current().getPendingNotificationRequests { requests in let notificationInfos = requests.map { request in [ "identifier": request.identifier, "title": request.content.title, "body": request.content.body, // 根据你的trigger类型保存对应参数,比如时间间隔 "timeInterval": (request.trigger as? UNTimeIntervalNotificationTrigger)?.timeInterval ?? 0 ] } UserDefaults.standard.set(notificationInfos, forKey: "PendingLocalNotifications") } } // 应用回到前台时调用 func applicationDidBecomeActive(_ application: UIApplication) { // 取出保存的本地通知信息,重新调度 guard let pendingNotifications = UserDefaults.standard.array(forKey: "PendingLocalNotifications") as? [[String: Any]] else { return } let center = UNUserNotificationCenter.current() for info in pendingNotifications { let content = UNMutableNotificationContent() content.title = info["title"] as? String ?? "" content.body = info["body"] as? String ?? "" content.sound = .default if let timeInterval = info["timeInterval"] as? TimeInterval { let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false) let request = UNNotificationRequest( identifier: info["identifier"] as? String ?? UUID().uuidString, content: content, trigger: trigger ) center.add(request) { error in if let error = error { print("重新调度本地通知失败:\(error.localizedDescription)") } } } } // 清空保存的信息 UserDefaults.standard.removeObject(forKey: "PendingLocalNotifications") } }
Android平台实现
Android的通知需要我们主动调用NotificationManager发送,因此可以先判断应用是否在前台,再决定是否发送本地通知;推送通知则保持后台正常展示,前台按需处理。
1. 判断应用前台状态工具方法
首先实现一个判断应用是否在前台的工具函数:
import android.app.ActivityManager import android.content.Context fun isAppInForeground(context: Context): Boolean { val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val runningProcesses = activityManager.runningAppProcesses ?: return false for (processInfo in runningProcesses) { if (processInfo.processName == context.packageName && processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { return true } } return false }
2. 本地通知发送控制
发送本地通知前先判断应用状态,前台则发送,后台则保存待发送:
import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.os.Build import androidx.core.app.NotificationCompat fun sendLocalNotification(context: Context, title: String, content: String) { if (!isAppInForeground(context)) { // 应用在后台,保存通知信息到SharedPreferences val sharedPref = context.getSharedPreferences("LocalNotifications", Context.MODE_PRIVATE) val editor = sharedPref.edit() val pendingNotifications = sharedPref.getStringSet("pendingNotifications", mutableSetOf()) ?: mutableSetOf() pendingNotifications.add("$title|$content") editor.putStringSet("pendingNotifications", pendingNotifications) editor.apply() return } // 应用在前台,发送本地通知 val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "local_notification_channel" // Android O及以上需要创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "本地通知", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(context, channelId) .setContentTitle(title) .setContentText(content) .setSmallIcon(R.drawable.ic_notification) // 替换成你的通知图标 .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() notificationManager.notify(1, notification) }
3. 前台恢复待发送的本地通知
通过LifecycleObserver监听应用回到前台的事件,发送保存的本地通知:
import android.content.Context import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner class AppForegroundObserver(private val context: Context) : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { super.onResume(owner) // 检查并发送待处理的本地通知 val sharedPref = context.getSharedPreferences("LocalNotifications", Context.MODE_PRIVATE) val pendingNotifications = sharedPref.getStringSet("pendingNotifications", mutableSetOf()) ?: mutableSetOf() if (pendingNotifications.isNotEmpty()) { val editor = sharedPref.edit() pendingNotifications.forEach { notificationStr -> val parts = notificationStr.split("|") if (parts.size == 2) { sendLocalNotification(context, parts[0], parts[1]) } } editor.remove("pendingNotifications") editor.apply() } } } // 在你的Application类中注册观察者 class MyApplication : Application() { override fun onCreate() { super.onCreate() ProcessLifecycleOwner.get().lifecycle.addObserver(AppForegroundObserver(this)) } }
4. 推送通知前台控制(以FCM为例)
如果使用FCM推送,在FirebaseMessagingService中判断前台状态,决定是否展示推送通知:
import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) if (isAppInForeground(this)) { // 应用在前台,不展示推送通知(如果需要前台展示,直接调用sendPushNotification即可) return } // 应用在后台,发送推送通知 sendPushNotification(remoteMessage) } private fun sendPushNotification(remoteMessage: RemoteMessage) { val title = remoteMessage.notification?.title ?: "推送通知" val content = remoteMessage.notification?.body ?: "新消息" val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "push_notification_channel" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "推送通知", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(this, channelId) .setContentTitle(title) .setContentText(content) .setSmallIcon(R.drawable.ic_notification) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() notificationManager.notify(2, notification) } }
内容的提问来源于stack exchange,提问作者Mluhammad Haris
相关产品推荐
相关产品推荐

