开发用于处理 MobileOperatorNotification 事件的应用

本主题说明如何开发处理 MobileOperatorNotification 事件的移动 宽带应用。

最佳做法

对于后台事件处理,应使用以下最佳做法:

  • 不要注册无法对其执行操作的后台事件。 处理这些事件会不必要地消耗应用配额。

  • 不要在收到后台事件时执行大量处理。

  • 请考虑将处理延迟到下次启动应用时。

  • 请考虑显示 Toast 通知并更新磁贴以响应后台事件。 移动宽带应用可以处理后台事件有效负载。

步骤 1:后台任务协定声明

要使 Windows 识别移动宽带应用提供的后台任务体验,应用必须声明它正在为系统功能提供扩展。

若要在 Visual Studio 项目的 package.appxmanifest 文件中进行声明,请执行以下步骤:

声明后台任务协定

  1. 解决方案资源管理器中,双击项目的 package.appxmanifest 文件。

  2. 在“ 声明 ”选项卡上的 “可用声明”中,选择“ 后台任务” ,然后单击“ 添加”。

  3. “属性” 标题下,输入以下应用信息:

    • 在“应用设置”下的“开始”页框中,对于使用 JavaScript 和 HTML 的移动宽带应用,输入处理应用 (后台任务的文件名,例如 ,backgroundtask.js) 。

    • “支持的任务类型”标题下,单击“系统事件检查”框。

如果正确完成此操作,则应在 package.appxmanifest 文件中具有类似于以下内容的扩展元素。

<Extension Category="windows.backgroundTasks" StartPage="backgroundtask.js">
  <BackgroundTasks>
    <Task Type="systemEvent" />
  </BackgroundTasks>
</Extension>

步骤 2:后台任务处理程序

如果你的应用提供移动运营商通知声明,它必须为后台任务激活提供处理程序。 处理程序将从 Windows.Networking.NetworkOperators.NetworkOperatorNotificationEventDetails 获取移动运营商网络帐户 ID 和事件数据。

由于后台任务支持的唯一 UI 是 Toast,因此后台任务处理程序可以显示 Toast 或将 NetworkOperatorNotificationEventDetails 保存到本地存储。

以下代码示例演示了一个后台任务,该任务设计为在收到新的管理短信通知时运行。

C#

using Windows.Networking.NetworkOperators;

namespace MNOMessageBackground
{
    public sealed class MNOBackgroundTask : IBackgroundTask
    {
       public void Run(Windows.ApplicationModel.Background.IBackgroundTaskInstance taskInstance)
       {
         NetworkOperatorNotificationEventDetails notifyData = (NetworkOperatorNotificationEventDetails)taskInstance.TriggerDetails;

         //The network account ID is stored in notifyData.NetworkAccountId

            switch (notifyData.NotificationType)
            {
                case NetworkOperatorEventMessageType.Gsm: // 0
                    break;
                case NetworkOperatorEventMessageType.Cdma: // 1
                    break;
                case NetworkOperatorEventMessageType.Ussd: // 2
                    break;
                case NetworkOperatorEventMessageType.DataPlanThresholdReached: // 3
                    break;
                case NetworkOperatorEventMessageType.DataPlanReset: //4 
                    break;
                case NetworkOperatorEventMessageType.DataPlanDeleted: //5
                    break;
                case NetworkOperatorEventMessageType.ProfileConnected: //6
                    break;
                case NetworkOperatorEventMessageType.ProfileDisconnected: //7
                    break;
                case NetworkOperatorEventMessageType.RegisteredRoaming: //8
                    break;
                case NetworkOperatorEventMessageType.RegisteredHome: ///9
                    break;
                case NetworkOperatorEventMessageType.TetheringEntitlementCheck: //10
                    break;

                default:
                    break;
             }

            // Add code to save the message to app local storage, and optionally show toast notification and tile updates.
        }
    }
}

JavaScript

(function () {
    "use strict";

    //
    // The background task instance's activation parameters are available via
    // Windows.UI.WebUI.WebUIBackgroundTaskInstance.current.
    //
    var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current,
        networkOperatorEventType = Windows.Networking.NetworkOperators.NetworkOperatorEventMessageType,
        key = null,
        settings = Windows.Storage.ApplicationData.current.localSettings;

    try {

        
        var details = backgroundTaskInstance.triggerDetails;

// The network account ID is stored in details.networkAccountId.

        switch (details.notificationType) {
            case networkOperatorEventType.gsm:
                showToast("Mobile Broadband message", details.message);
                break;
            case networkOperatorEventType.cdma:
                showToast("Mobile Broadband message", details.message);
                break;
            case networkOperatorEventType.ussd:
                showToast("Mobile Broadband message", details.message);
                break;
            case networkOperatorEventType.dataPlanThresholdReached:
                showToast("Mobile Broadband message", "Data plan threshold reached");
                break;
            case networkOperatorEventType.dataPlanReset:
                showToast("Mobile Broadband message", "Data plan reset");
                break;
            case networkOperatorEventType.dataPlanDeleted:
                showToast("Mobile Broadband message", "Data plan deleted");
                break;
            case networkOperatorEventType.profileConnected:
                showToast("Mobile Broadband message", "Profile connected");
                break;
            case networkOperatorEventType.profileDisconnected:
                showToast("Mobile Broadband message", "Profile disconnected");
                break;
            case networkOperatorEventType.registeredRoaming:
                showToast("Mobile Broadband message", "Registered roaming");
                break;
            case networkOperatorEventType.registeredHome:
                showToast("Mobile Broadband message", "Registered home");
                break;
            case networkOperatorEventType.tetheringEntitlementCheck:
                showToast("Mobile Broadband message", "Entitlement check completed");
                break;
            default:
                showToast("Mobile Broadband message", "Unknown message");
                break;
        }

        //
        // A JavaScript background task must call close when it is done.
        //
 close();
    }
    catch (exception) {
// Display error message.
close();
    }

显示磁贴和 Toast 通知

建议在移动宽带应用中同时显示 Toast 和磁贴通知,因为用户可能会因为 Toast 通知的暂时性而错过该通知。 有关 Toast 通知和磁贴更新体验设计指南,请参阅 设计移动宽带应用的用户体验

启用 Toast 通知

  1. 解决方案资源管理器中,双击项目的 package.appxmanifest 文件。

  2. 在“ 应用程序 UI ”选项卡上的“ 通知 ”标题下,将 Toast“支持 ”设置为 “是”。

如果正确完成此操作,则应在 package.appxmanifest 文件中具有类似于以下内容的扩展元素。

<VisualElements … ToastCapable="true"… />

以下代码演示如何在后台任务句柄中显示 Toast 通知:

JavaScript

function showToast(title, body) {
        var notifications = Windows.UI.Notifications;
        var toastNotificationManager = Windows.UI.Notifications.ToastNotificationManager;
        var toastXml = toastNotificationManager.getTemplateContent(notifications.ToastTemplateType.toastText02);

        var temp = "the parameter will pass to app when app activated from tap Toast ";
        toastXml.selectSingleNode("/toast").setAttribute("launch", temp);

        var textNodes = toastXml.getElementsByTagName("text");
        textNodes[0].appendChild(toastXml.createTextNode(title));
        textNodes[1].appendChild(toastXml.createTextNode(body));

        var toast = new notifications.ToastNotification(toastXml);
        toastNotificationManager.createToastNotifier().show(toast);
    }

获取短信

如果后台任务是由传入的短信触发的,则后台任务详细信息将在其有效负载中携带 SMS 对象。

JavaScript

(function () {
    "use strict";

    //
    // The background task instance's activation parameters are available via
    // Windows.UI.WebUI.WebUIBackgroundTaskInstance.current.
    //
    var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current,

    try {
        
        var details = backgroundTaskInstance.triggerDetails;
        if (details.notificationType === networkOperatorEventType.gsm
        || details.notificationType === networkOperatorEventType.cdma)
        {
     var textMessage = new Windows.Devices.Sms.SmsTextMessage.fromBinaryMessage(details.smsMessage);
            
         // textMessage can be used to get other SmsMessage properties    
         // like sender number, timestamp, message part count etc.
         showToast("From: " + textMessage.from + "; TimeStamp: " + textMessage.timestamp, details.message);
        }

使用本地存储

后台任务可以使用本地存储来保存从后台事件获取的消息,以便应用以后可以使用该信息。

JavaScript

    //
    // Save the message 
    //
    var settings = Windows.Storage.ApplicationData.current.localSettings;
    var keyMessage = "BA5857FA-DE2C-4A4A-BEF2-49D8B4130A39";


    //
    // The background task instance's activation parameters are available via
    // Windows.UI.WebUI.WebUIBackgroundTaskInstance.current
    //
    var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;

    var details = backgroundTaskInstance.triggerDetails;
    settings.values[keyMessage] = details.message;

以下代码演示如何检索后台任务处理程序在应用中存储的消息:

JavaScript

var settings = Windows.Storage.ApplicationData.current.localSettings;
    var keyMessage = "BA5857FA-DE2C-4A4A-BEF2-49D8B4130A39";
    var operatorMessage = settings.values[keyMessage];

步骤 3:处理激活事件

如果 toast 设置参数,它将通过 detail.arguments 传递给应用。

在 JavaScript 或 C# 中,处理 WinJS.Application.onactivated 事件,然后检查传递给事件处理程序的事件参数。 从 Toast 激活传递 类型为 Windows.UI.WebUI.WebUILaunchActivatedEventArgs 的事件参数。 如果事件参数的 detail.kind 属性为 Windows.ApplicationModel.Activation.ctivationKind启动,应用提供“开始”体验或通知体验,具体取决于事件参数的 detail.argument 属性是否设置为 null

JavaScript

WinJS.Application.addEventListener("activated", activated; false);

function activated(eventArgs)
{
  if (eventArgs.detail.kind == Windows.ApplicationModel.Activation.ActivationKind.launch)
  {
    if (!eventArgs.detail.arguments)
    {
      // Initialize logic for the Start experience here.
    }
    else
    {
      // Initialize logic for the Notification experience here.
    }
  }
}

步骤 4:处理后台任务完成处理程序

前台应用还可以注册完成处理程序,以便在后台任务完成时收到通知。 后台任务的 Run 方法中发生的完成状态或任何异常将传递到前台应用中的完成处理程序。 如果应用在任务完成时暂停,则下次恢复应用时会收到完成通知。 如果应用处于 “已终止” 状态,则不会收到完成通知。 如果后台任务需要保留成功运行的信息,则必须使用状态管理器或其他方式(例如应用在返回到 “正在运行” 状态时读取的文件)来保留信息。

重要 尽管移动运营商后台事件由系统自动注册到应用,但应用仍需要至少运行一次才能注册到后台完成或进度处理程序。

C#

foreach (var cur in BackgroundTaskRegistration.AllTasks)
{
   if(cur.Value.Name == “MobileOperatorNotificationHandler”)
   {
       cur.Value.Progress += new BackgroundTaskProgressEventHandler(OnProgress);
       cur.Value.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
   }
}

//
// Handle background task completion.
private void OnCompleted(IBackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs e)
{
   var taskCompletion = task as IBackgroundTaskRegistration;
   var completionArgs = args.Context as BackgroundTaskCompletedEventArgs;
   
  //
  // If the background task threw an exception, display the exception in the error text box.
  if (completionArgs.Status != null)
  {
    throw completionArgs.Status;
  }
}

// Handle background task progress.
private void OnProgress(IBackgroundTaskRegistration sender, BackgroundTaskProgressEventArgs e)
{
  var taskRegistration = task as IBackgroundTaskRegistration;
  var progressArgs = args.Context as BackgroundTaskProgressEventArgs;
  // progressArgs.Progress has the progress percentage
}

JavaScript

var iter = Windows.ApplicationModel.Background.BackgroundTaskRegistration.allTasks.first();
var hascur = iter.hasCurrent;
while (hascur) {
    var cur = iter.current.value;
    if (cur.name === “MobileOperatorNotificationHandler”) {
        cur.addEventListener("progress", new ProgressHandler(cur).onProgress);
        cur.addEventListener("completed", new CompleteHandler(cur).onCompleted);
    }
    hascur = iter.moveNext();
}

//
// Handle background task progress.
//
function ProgressHandler(task) {
    this.onProgress = function (args) {
       try {
           var progress = "Progress: " + args.progress + "%";
       } catch (ex) {
           displayError(ex);
       }
   };
}

//
// Handle background task completion.
//
function CompleteHandler(task) {
    this.onCompleted = function (args) {
        try {
            var key = task.taskId;
        } catch (ex) {
            displayError(ex);
        }
    };
}

故障排除

使用这些部分来帮助排查可能出现的问题。

触发元数据分析以注册后台任务

对于用户,连接移动宽带设备时,Windows 8、Windows 8.1和Windows 10会自动安装移动宽带应用和关联的服务元数据,并注册服务元数据中定义的后台任务。 但是,在Windows 8.1中,应用不会自动固定到“开始”屏幕。

开发人员可以通过按 F5 键 (手动触发Windows 8、Windows 8.1和Windows 10来分析服务元数据和注册后台任务,或者右键单击并选择桌面上的“设备和打印机”窗口中的“刷新) ”。 仅当部署应用时,通过服务元数据分析进行后台任务注册才成功。

验证是否已正确注册后台任务

开发人员可以通过查看 Application and Services Logs\Microsoft\Windows\DeviceSetupManager 下的事件日志来验证设备安装程序管理器 (DSM) 是否已正确分析服务元数据。

  1. 打开事件查看器。

  2. “菜单 ”选项卡上,选择“ 视图”,然后单击“ 显示分析和调试日志”。

  3. 浏览到 “应用程序和服务日志\Microsoft\Windows\DeviceSetupManager”。

感兴趣的事件包括指示 DSM 已成功注册 MobileOperatorNotification 事件的后台任务的事件 ID 220 和事件 ID 7900(指示在元数据包中找到的任何错误)。

验证预配元数据是否已成功应用

应用预配元数据时,请验证 ProvisionFromXmlDocumentResults.AllElementsProvisioned 是否为 true。 如果没有,检查 ProvisionResultsXml 以获取有关错误的详细信息。 常见的移动宽带错误包括:

  • 电脑中的 SIM 卡与配置文件 (配置文件不匹配,ERROR_NOT_FOUND) 失败。

  • 预配文件中的 CarrierId 与体验元数据中的服务编号不匹配。

验证后台任务是否由系统事件代理运行

可以通过检查事件查看器来验证 Windows 是否正在生成 MobileOperatorNotification 事件,以及应用后台任务是否由事件代理运行。 这些事件的日志记录默认处于关闭状态,可以通过执行以下步骤来启用:

  1. 打开事件查看器。

  2. “菜单 ”选项卡上,选择“ 视图”,然后单击“ 显示分析和调试日志”。

  3. 浏览到 “应用程序和服务日志\Microsoft\Windows\BackgroundTaskInfrastructure”。

  4. 右键单击“ 诊断日志 ”,然后选择“ 启用日志”。

启用日志后,成功执行后台任务将导致事件 ID = 1 的事件,该事件具有以下说明:“具有入口点<的后台任务的实例background_task_namespace_name>。<> background_task_class_name和名称 MobileOperatorNotificationHandler 已在会话 1 中创建,并给定的 {11111111-1111-1111-1111-111111111111}ID 为 。”

如果未运行后台任务,请首先验证在服务元数据中指定的后台任务的名称是否与包的 AppXManifest.xml 文件中的名称匹配。 验证部署应用后,是否触发分析服务元数据并插入移动宽带设备。

验证 Windows 是否收到短信和 USSD 通知

可以通过检查 事件查看器 中的 SmsRouter 事件来验证 Windows 是否正在接收短信和 USSD 通知。

在事件查看器,在“应用程序和服务日志\Microsoft\Windows\Mobile-Broadband-Experience-SmsRouter\Microsoft-Windows-SMSRouter”下,是“SMSRouter 已收到短信通知消息”和“SMSRouter 已收到短信”等条目。 在 “应用程序和服务日志\Microsoft\Windows\Mobile-Broadband-Experience-SmsApi\SMSApi ”下,条目如下:“应用:Microsoft.SDKSamples.SmsSendReceive 在移动宽带设备上发送了短信: {11111111-1111-1111-1111-111111111111}”。

接收的短信未检测为操作员通知

如果收到的短信未检测为操作员通知,请在帐户预配元数据中验证管理短信通知的自定义筛选规则。 有关预配元数据的详细信息,请参阅 帐户预配

具体而言,如果帐户预配元数据指定了发件人电话号码,请使用 SMS API 验证指定的号码格式是否与收到的消息中的格式匹配。 若要验证此是否正确匹配,请暂时将模式更改为 [^]\* 以匹配来自此发件人的所有邮件。

示例 backgroundtask.js 文件

//
// A JavaScript background task runs a specified JavaScript file.
//
(function () {
    "use strict";

    //
    // The background task instance's activation parameters are available via Windows.UI.WebUI.WebUIBackgroundTaskInstance.current.
    //
    var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current,
        networkOperatorEventType = Windows.Networking.NetworkOperators.NetworkOperatorEventMessageType,
        key = null,
        settings = Windows.Storage.ApplicationData.current.localSettings;

    try {       
        var details = backgroundTaskInstance.triggerDetails;

        switch (details.notificationType) {
            case networkOperatorEventType.gsm:
                var textMessage = new Windows.Devices.Sms.SmsTextMessage.fromBinaryMessage(details.smsMessage);
                showToast("Gsm Msg From: " + textMessage.from + "; TimeStamp: " + textMessage.timestamp, details.message);
                
                break;
            case networkOperatorEventType.cdma:
                showToast("Mobile Broadband message", details.message);
                break;
            case networkOperatorEventType.ussd:
                showToast("Mobile Broadband message", details.message);
                break;
            case networkOperatorEventType.dataPlanThresholdReached:
                showToast("Mobile Broadband message", "Data plan threshold reached");
                break;
            case networkOperatorEventType.dataPlanReset:
                showToast("Mobile Broadband message", "Data plan reset");
                break;
            case networkOperatorEventType.dataPlanDeleted:
                showToast("Mobile Broadband message", "Data plan deleted");
                break;
            case networkOperatorEventType.profileConnected:
                showToast("Mobile Broadband message", "Profile connected");
                break;
            case networkOperatorEventType.profileDisconnected:
                showToast("Mobile Broadband message", "Profile disconnected");
                break;
            case networkOperatorEventType.registeredRoaming:
                showToast("Mobile Broadband message", "Registered roaming");
                break;
            case networkOperatorEventType.registeredHome:
                showToast("Mobile Broadband message", "Registered home");
                break;
            case networkOperatorEventType.tetheringEntitlementCheck:
                showToast("Mobile Broadband message", "Entitlement check completed");
                break;
            default:
                showToast("Mobile Broadband message", "Unknown message");
                break;
        }
        taskSucceeded();
    }
    catch (exception) {
        taskFailed();
    }

    function showToast(title, body) {

        var notifications = Windows.UI.Notifications;
        var toastNotificationManager = Windows.UI.Notifications.ToastNotificationManager;
        var toastXml = toastNotificationManager.getTemplateContent(notifications.ToastTemplateType.toastText02);

        //
        // Pass to app through eventArguments.arguments.
        //
        var temp = "\"Title\"" + ":" + "\"" + title + "\"" + "," + "\"Message\"" + ":" + "\"" + body + "\"";
        if (temp.length > 251) {
            temp = temp.substring(0, 251);
        }
        toastXml.selectSingleNode("/toast").setAttribute("launch", "'{" + temp + "}'");

        var textNodes = toastXml.getElementsByTagName("text");
        textNodes[0].appendChild(toastXml.createTextNode(title));
        textNodes[1].appendChild(toastXml.createTextNode(body));

        var toast = new notifications.ToastNotification(toastXml);
        toastNotificationManager.createToastNotifier().show(toast);        
    }

    //
    // This function is called when the background task is completed successfully.
    //
    function taskSucceeded() {
        //
        // Use the succeeded property to indicate that this background task completed successfully.
        //
        backgroundTaskInstance.succeeded = true;
        backgroundTask.taskInstance.progress = 100;
        console.log("Background " + backgroundTask.taskInstance.task.name + " Completed");

        //
        // Write to localSettings to indicate that this background task completed.
        //
        key = backgroundTaskInstance.task.taskId.toString();
        settings.values[key] = "Completed";

        //
        // A JavaScript background task must call close when it is done.
        //
        close();
    }

    //
    // If the task was canceled or failed, stop the background task.
    //
    function taskFailed() {
        console.log("Background " + backgroundTask.taskInstance.task.name + " Failed");
        backgroundTaskInstance.succeeded = false;

        key = backgroundTaskInstance.task.taskId.toString();
        settings.values[key] = "Failed";

        close();
    }
})();

启用移动运营商通知和系统事件

创建和配置 Internet 共享体验