跳转到主要内容

概述

通知服务扩展允许您在向用户显示推送通知之前拦截和修改推送通知。这使得以下功能成为可能:
  • 后台数据处理
  • 自定义样式(颜色、图标、振动)
  • 富媒体附件(图片、视频)
  • 确认送达/分析
  • 操作按钮选项
您可以通过 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*/
     }
}

2. 示例用例

以下是您可以在上述模板通知服务扩展类中实现的常见示例。
  • 阻止通知显示
  • 添加自定义字段
  • 更改通知颜色和图标
event.preventDefault();

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

    //手动显示通知
    event.getNotification().display();
}).start();

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

AndroidManifest.xml 文件的 application 标签中添加类名和值作为 meta-data。忽略任何”未使用”警告。
XML
<application>
  <!-- 保持 android:name 如图所示,将 android:value 设置为您的类的完整命名空间-->
  <meta-data
    android:name="com.onesignal.NotificationServiceExtension"
    android:value="com.onesignal.example.NotificationServiceExtension" />
</application>

iOS 通知服务扩展

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

获取 iOS 推送载荷

在遵循移动 SDK 设置时,您将看到有关将代码添加到 OneSignalNotificationServiceExtension 的部分。 在该代码中,有一个 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"]
}
我们可以通过使用 a 参数的 custom 对象在 OneSignalNotificationServiceExtension 中访问此附加 data
// Check if `bestAttemptContent` exists
if let bestAttemptContent = bestAttemptContent {

    // Try to retrieve the "custom" data from the notification payload
    if let customData = bestAttemptContent.userInfo["custom"] as? [String: Any],
       let additionalData = customData["a"] as? [String: Any] {

        // Convert the `additionalData` dictionary to a JSON string for logging
        if let jsonData = try? JSONSerialization.data(withJSONObject: additionalData, options: .prettyPrinted),
           let jsonString = String(data: jsonData, encoding: .utf8) {
            // Successfully converted to JSON; log the formatted JSON string
            print("The additionalData dictionary in JSON format:\n\(jsonString)")
        } else {
            // Failed to convert the `additionalData` dictionary to JSON
            print("Failed to convert additionalData to JSON format.")
        }
    }

    // Try to retrieve the "aps" data from the notification payload
    if let messageData = bestAttemptContent.userInfo["aps"] as? [String: Any],
       let apsData = messageData["alert"] as? [String: Any],
       let body = apsData["body"] as? String,  // Extract the "body" of the alert
       let title = apsData["title"] as? String {  // Extract the "title" of the alert
        // Successfully retrieved the body and title; log the values
        print("The message contents is: \(body), message headings is: \(title)")
    } else {
        // Failed to retrieve the "aps" data or its contents
        print("Unable to retrieve apsData")
    }

    // Pass the notification to OneSignal for further processing
    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 中的主应用目标示例

Xcode 中的 OneSignalNotificationServiceExtension 目标示例

继续在 OneSignalNotificationServiceExtension > Info 标签中,展开 NSExtension 键。确保您看到:
XML
 <dict>
   <key>NSExtensionPointIdentifier</key>
   <string>com.apple.usernotifications.service</string>
   <key>NSExtensionPrincipalClass</key>
   <string>$(PRODUCT_MODULE_NAME).NotificationService</string>
 </dict>
示例:

Info 标签中的 NSExtension 键示例

如果使用 Objective-C,请使用 NotificationService 而不是 $(PRODUCT_MODULE_NAME).NotificationService
关闭”Copy only when installing”(仅在安装时复制) 选择您的主应用目标 > Build Phases > Embed App Extensions。确保”Copy only when installing”(仅在安装时复制)未选中。如果已选中,请取消选中:

主应用目标构建阶段设置

调试 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)")
        //debug log types need to be enabled in Console > Action > Include Debug Messages
        //Replace YOUR_BUNDLE_ID with your actual Bundle ID
        os_log("%{public}@", log: OSLog(subsystem: "YOUR_BUNDLE_ID", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription)

        if let bestAttemptContent = bestAttemptContent {
            /* DEBUGGING: Uncomment the 2 lines below to check this extension is executing
                          Note, this extension only runs when mutable-content is set
                          Setting an attachment or action buttons automatically adds this */
            print("Running NotificationServiceExtension")
            bestAttemptContent.body = "[Modified] " + bestAttemptContent.body

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

    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
            contentHandler(bestAttemptContent)
        }
    }

}

2. 更改活动方案

将您的活动方案设置为 OneSignalNotificationServiceExtension

Xcode 活动方案选择

3. 构建并运行项目

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

4. 打开控制台

在 Xcode 中,选择 Window > Devices and Simulators

Xcode 设备和模拟器窗口

您应该会看到您的设备已连接。选择 Open Console(打开控制台)。

设备控制台访问按钮

5. 检查控制台

在控制台中:
  • 选择 Action > Include Debug Messages(操作 > 包含调试消息)
  • 搜索 OneSignalNotificationServiceExtension 作为 CATEGORY(类别)
  • 选择 Start(开始)

控制台调试配置

向此设备发送一条带有消息的通知(如果从 Create message API 发送,请使用 contents 属性)。在此示例中,载荷为:
cURL
  //Replace with your own app data:
  //YOUR_API_KEY, YOUR_APP_ID, SUBSCRIPTION_ID_1

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"
]
}'
您应该会看到在应用运行和未运行时记录的消息。

控制台调试输出示例

如果您没有看到消息,请从您的应用中删除 OneSignal,并再次按照我们的移动 SDK 设置进行操作,以验证您是否正确设置了 OneSignal。