You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:41:35