跳转到主要内容
通知服务扩展允许您在向用户显示推送通知之前拦截和修改推送通知。
您可以通过 OSNotification 类访问从 OneSignal 发送的推送通知中的数据

Android 通知服务扩展

允许您在向用户显示通知之前处理通知。常见用例包括:
  • 在后台接收数据,可以显示或不显示通知。
  • 根据客户端应用逻辑覆盖特定通知设置,例如自定义强调色、振动模式或任何其他可用的 NotificationCompat 选项。
有关更多详细信息,请参阅 Android 关于 NotificationCompat 选项的文档

第 1 步:为服务扩展创建一个类

创建一个扩展 INotificationServiceExtension 的类并实现 onNotificationReceived 方法。 方法 onNotificationReceived 参数是 event,类型为 INotificationReceivedEvent
package your.package.name

import androidx.annotation.Keep;
import com.onesignal.notifications.IActionButton;
import com.onesignal.notifications.IDisplayableMutableNotification;
import com.onesignal.notifications.INotificationReceivedEvent;
import com.onesignal.notifications.INotificationServiceExtension;

@Keep // Keep is required to prevent minification from renaming or removing your class
public class NotificationServiceExtension implements INotificationServiceExtension {

     @Override
     public void onNotificationReceived(INotificationReceivedEvent event) {
        IDisplayableMutableNotification notification = event.getNotification();

        if (notification.getActionButtons() != null) {
           for (IActionButton button : notification.getActionButtons()) {
              // you can modify your action buttons here
           }
        }

     /* Add customizations here. See examples below for additional methods to modify the notification*/
     }
}

@Keep 注解可防止 ProGuard/R8 在代码压缩期间重命名或移除您的类。

第 2 步:自定义通知

以下是您可以在上述模板通知服务扩展类中实现的常见示例。
调用 event.preventDefault() 可以阻止通知自动显示,然后决定是手动显示还是完全不显示。
event.preventDefault();

//执行一些异步工作,然后决定显示或关闭
new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException ignored) {}

    //手动显示通知
    event.getNotification().display();
}).start();
调用 event.preventDefault() 后,如果您从未调用 display(),通知将被静默丢弃,不会有任何提示。详情请参阅重复通知

第 3 步:将服务扩展添加到您的 AndroidManifest.xml

AndroidManifest.xml 文件的 application 标签中添加类名和值作为 meta-data。忽略任何”未使用”警告。
XML
<application>
  <meta-data
    android:name="com.onesignal.NotificationServiceExtension"
    android:value="com.onesignal.example.NotificationServiceExtension" />
</application>
com.onesignal.example.NotificationServiceExtension 替换为您的类的完全限定名称。

iOS 通知服务扩展

UNNotificationServiceExtension 允许您在向用户显示推送通知之前修改推送通知的内容,并且是其他重要功能所必需的,例如: 如果您遵循了我们针对您的应用的移动 SDK 设置说明,扩展应该已经配置好了。本节将说明如何访问 OneSignal 通知有效负载数据并解决您可能遇到的任何问题。

获取 iOS 推送载荷

didReceive(_:withContentHandler:) 的重写会调用 OneSignalExtension.didReceiveNotificationExtensionRequest。在调用该方法之前,您可以读取或修改 bestAttemptContent 在此示例中,我们发送包含以下数据的通知:
JSON
{
  "app_id": "YOUR_APP_ID",
  "target_channel": "push",
  "headings": {"en": "The message title"},
  "contents": {"en": "The message contents"},
  "data":{
    "additional_data_key_1":"value_1",
    "additional_data_key_2":"value_2"
    },
  "include_subscription_ids": ["SUBSCRIPTION_ID_1"]
}
通过 userInfocustom 字典中的 a 键,在 OneSignalNotificationServiceExtension 中访问此附加 data
if let bestAttemptContent = bestAttemptContent {

    if let customData = bestAttemptContent.userInfo["custom"] as? [String: Any],
       let additionalData = customData["a"] as? [String: Any] {

        if let jsonData = try? JSONSerialization.data(withJSONObject: additionalData, options: .prettyPrinted),
           let jsonString = String(data: jsonData, encoding: .utf8) {
            print("The additionalData dictionary in JSON format:\n\(jsonString)")
        } else {
            print("Failed to convert additionalData to JSON format.")
        }
    }

    if let messageData = bestAttemptContent.userInfo["aps"] as? [String: Any],
       let apsData = messageData["alert"] as? [String: Any],
       let body = apsData["body"] as? String,
       let title = apsData["title"] as? String {
        print("The message contents is: \(body), message headings is: \(title)")
    } else {
        print("Unable to retrieve apsData")
    }

    OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest,
                                                              with: bestAttemptContent,
                                                              withContentHandler: self.contentHandler)
}
控制台输出示例:
The additionalData dictionary in JSON format:
{
  "additional_data_key_1" : "value_1",
  "additional_data_key_2" : "value_2"
}
The message contents is: The message contents, message headings is: The message title

iOS 通知服务扩展故障排除

本指南用于调试 iOS 移动应用上未显示图片、操作按钮或确认送达的问题。

检查您的 Xcode 设置

General > Targets 中确保您的主应用目标OneSignalNotificationServiceExtension 目标具有相同且正确的:
  • Supported Destinations(支持的目标)
  • Minimum Deployment(最低部署版本,iOS 14.5 或更高版本)
如果您使用的是 Cocoapods,请确保这些与 Podfile 中的主目标匹配,以避免构建错误。
Xcode General tab showing Supported Destinations and Minimum Deployment for the main app target
Xcode General tab showing Supported Destinations and Minimum Deployment for the notification service extension target
继续在 OneSignalNotificationServiceExtension > Info 标签中,展开 NSExtension 键。确保您看到:
XML
 <dict>
   <key>NSExtensionPointIdentifier</key>
   <string>com.apple.usernotifications.service</string>
   <key>NSExtensionPrincipalClass</key>
   <string>$(PRODUCT_MODULE_NAME).NotificationService</string>
 </dict>
示例:
Xcode Info tab showing the NSExtension dictionary with NSExtensionPointIdentifier and NSExtensionPrincipalClass keys
如果使用 Objective-C,请使用 NotificationService 而不是 $(PRODUCT_MODULE_NAME).NotificationService

关闭”Copy only when installing”(仅在安装时复制)

选择您的主应用目标 > Build Phases > Embed App Extensions。确保”Copy only when installing”(仅在安装时复制)未选中。如果已选中,请取消选中:
Xcode Build Phases tab showing Embed App Extensions with Copy only when installing unchecked

调试 iOS 通知服务扩展

按照以下步骤验证通知服务扩展是否正确设置。

1. 更新 OneSignalNotificationServiceExtension 代码

打开 NotificationService.mNotificationService.swift,并将整个文件内容替换为以下代码。这将添加日志记录以帮助验证扩展是否正在运行。 YOUR_BUNDLE_ID 替换为您的实际 Bundle ID。
import UserNotifications
import OneSignalExtension
import os.log

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var receivedRequest: UNNotificationRequest!
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.receivedRequest = request
        self.contentHandler = contentHandler
        self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        let userInfo = request.content.userInfo
        let custom = userInfo["custom"]
        print("Running NotificationServiceExtension: userInfo = \(userInfo.description)")
        print("Running NotificationServiceExtension: custom = \(custom.debugDescription)")
        os_log("%{public}@", log: OSLog(subsystem: "YOUR_BUNDLE_ID", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription)

        if let bestAttemptContent = bestAttemptContent {
            print("Running NotificationServiceExtension")
            bestAttemptContent.body = "[Modified] " + bestAttemptContent.body

            OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
            contentHandler(bestAttemptContent)
        }
    }

}

调试日志类型需要在控制台中通过 Action > Include Debug Messages 启用。

2. 更改活动方案

将您的活动方案设置为 OneSignalNotificationServiceExtension
Xcode toolbar showing the active scheme dropdown set to OneSignalNotificationServiceExtension

3. 构建并运行项目

在 Xcode 中在真实设备上构建并运行项目。

4. 打开控制台

在 Xcode 中,选择 Window > Devices and Simulators
Xcode menu showing the Window dropdown with Devices and Simulators selected
您应该会看到您的设备已连接。选择 Open Console(打开控制台)。
Xcode Devices window showing the Open Console button for a connected device

5. 检查控制台

在控制台中:
  1. 选择 Action > Include Debug Messages(操作 > 包含调试消息)
  2. 搜索 OneSignalNotificationServiceExtension 作为 CATEGORY(类别)
  3. 选择 Start(开始)
macOS Console app showing the category filter and Start button for debugging the notification service extension
向此设备发送一条带有消息的通知(如果从推送通知 API 发送,请使用 contents 属性)。在此示例中,载荷为:
cURL
curl --request POST \
 --url 'https://api.onesignal.com/notifications' \
 --header 'Authorization: Key YOUR_API_KEY' \
 --header 'accept: application/json' \
 --header 'content-type: application/json' \
 --data '
{
"app_id": "YOUR_APP_ID",
"target_channel": "push",
"headings": {"en": "The message title"},
"contents": {"en": "The message contents"},
"data":{"additional_data_key_1":"value_1","additional_data_key_2":"value_2"},
"include_subscription_ids": [
"SUBSCRIPTION_ID_1"
]
}'
您应该会看到在应用运行和未运行时记录的消息。
macOS Console app showing debug log output from the OneSignalNotificationServiceExtension
如果您没有看到消息,请从您的应用中删除 OneSignal,并再次按照移动 SDK 设置进行操作,以验证集成是否正确。

常见问题

为什么我的通知服务扩展在 iOS 上无法运行? 扩展仅在通知设置了 mutable-content 时才会运行。OneSignal 会自动为包含附件或操作按钮的通知设置此项。请验证您的 Xcode 设置是否与故障排除部分中的要求一致。 我可以在 Android 上阻止通知显示吗? 可以,调用 event.preventDefault() 可以阻止通知显示。之后可以调用 event.getNotification().display() 手动显示,或者不调用以静默丢弃通知。详情请参阅重复通知 Android 上是否需要 @Keep 注解? 是的,@Keep 注解可防止 ProGuard/R8 在代码压缩期间重命名或移除实现了 INotificationServiceExtension 的类。

相关页面

移动 SDK 设置

为 iOS 和 Android 安装和配置 OneSignal SDK。

图片与富媒体

为推送通知附加图片、GIF 和视频。

确认送达

跟踪通知到达设备的确认送达情况。

重复通知

解决所有平台上的重复推送通知问题。