Xamarin 中的 watchOS 后台任务watchOS Background Tasks in Xamarin

使用 watchOS 3,手表应用可通过以下三种主要方式将其信息保持在最新状态:With watchOS 3, there are three main ways that a watch app can keep its information up-to-date:

  • 使用几个新的后台任务之一。Using one of the several new background tasks.
  • 在手表面上有一个复杂的问题(为它提供额外的更新时间)。Having one of its Complications on the watch face (giving it extra time to update).
  • 让用户将应用固定到新的停靠(其保留在内存中并经常更新)。Having the user pin to app to the new Dock (where its kept in memory and updated often).

使应用保持最新状态Keeping an App Up-To-Date

在讨论开发人员可以让 watchOS 应用程序的数据和用户界面保持最新和更新的所有方法之前,此部分将介绍一组典型的使用模式,以及用户如何在其 iPhone 及其 Apple Watch 之间根据 当天的时间和当前正在执行的活动(如驱动)。Before discussing all of the ways a developer can keep a watchOS app's data and User Interface current and updated, this section will take a look at a typical set of use patterns and how a user might move between their iPhone and their Apple Watch throughout the day based on the time of day and the activity they are currently doing (such as driving).

请参见以下示例:Take the following example:

  1. 在早上,在等待咖啡时,用户会在其 iPhone 上浏览当前新闻几分钟。In the morning, while waiting in line for a coffee, the user browses the current news on their iPhone for several minutes.
  2. 在离开咖啡店之前,他们会快速查看天气,并看看其观看面上的难点。Before leaving the coffee shop, they quickly check the weather with a Complication on their watch face.
  3. 就餐之前,他们使用 iPhone 上的 Maps 应用来查找附近的餐馆,并预订保留以满足客户端的需要。Before lunch, they use the Maps app on the iPhone to find an nearby restaurant and book a reservation to meet a client.
  4. 旅行给餐馆后,他们会收到有关其 Apple Watch 的通知,并通过快速浏览了解其午餐约会是否正在延迟。While traveling to the restaurant, they get a notification on their Apple Watch and with a quick glance, they know their lunch appointment is running late.
  5. 在晚上,它们使用 iPhone 上的 Maps 应用来检查流量。In the evening, they use the Maps app on the iPhone to check traffic before driving home.
  6. 在家里,它们会在其 Apple Watch 上收到 iMessage 通知,要求他们选取一些牛奶,并使用 "快速答复" 功能将响应发送到 "正常"。On the way home, they receive an iMessage notification on their Apple Watch asking them to pick up some milk and they use the Quick Reply feature to send the response "OK".

由于 "速览" (小于三秒),用户想要如何使用 Apple Watch 应用程序,因此应用程序在向用户显示所需的信息和更新其 UI 之前,通常没有足够的时间。Because of the "quick glance" (less than three seconds) nature of how a user is wanting to use an Apple Watch app, there typically isn't enough time for the app to fetch desired information and update its UI before displaying it to the user.

使用新的 Api Apple 已包含在 watchOS 3 中,应用可以计划_后台刷新_,并在用户请求之前准备好所需的信息。By using the new APIs Apple has included in watchOS 3, the app can schedule for a Background Refresh and have the desired information ready before the user requests it. 采用上述天气的示例:Take the example of the Weather Complication discussed above:

  1. 应用计划在特定时间唤醒系统。The app schedules to be woken up by the system at a specific time.
  2. 此应用将获取生成更新时所需的信息。The app fetches the information that it will require to generate the update.
  3. 应用程序重新生成其用户界面,以反映新的数据。The app regenerates its User Interface to reflect the new data.
  4. 当用户深入了解应用程序时,它具有最新信息,用户无需等待更新。When the user glances at the app's Complication, it has up-to-date information without the user having to wait for the update.

如上所示,watchOS 系统使用一个或多个任务唤醒应用程序,该应用程序具有非常有限的池可用:As seen above, the watchOS system wakes the app using one or more Tasks, of which it has a very limited pool available:

Apple 建议在应用完成自行更新过程之前,充分利用此任务(因为它是应用的有限资源)。Apple suggest making the most of this Task (since it is such a limited resource to the app) by holding onto it until the app has finished the process of updating itself.

系统通过调用 WKExtensionDelegate 委托的新 HandleBackgroundTasks 方法来提供这些任务。The system delivers these Tasks by calling the new HandleBackgroundTasks method of the WKExtensionDelegate delegate. 例如:For example:

using System;
using Foundation;
using WatchKit;

namespace MonkeyWatch.MonkeySeeExtension
{
  public class ExtensionDelegate : WKExtensionDelegate
  {
    #region Constructors
    public ExtensionDelegate ()
    {
    }
    #endregion

    #region Override Methods
    public override void HandleBackgroundTasks (NSSet<WKRefreshBackgroundTask> backgroundTasks)
    {
      // Handle background request here
      ...
    }
    #endregion
  }
}

当应用完成了给定任务后,它会将其标记为 "已完成",从而将其返回给系统:When the app has finished the given Task, it returns it to the system by marking it completed:

新后台任务New Background Tasks

watchOS 3 引入了多个后台任务,应用可以使用这些任务来更新其信息,确保在打开应用之前,该用户具有用户需要的内容,例如:watchOS 3 introduces several background tasks that an app can use to update its information ensuring that it has the content the user needs before they open the app, such as:

以下各节将详细介绍这些任务。These Tasks will be covered in detail in the sections below.

WKApplicationRefreshBackgroundTaskWKApplicationRefreshBackgroundTask

WKApplicationRefreshBackgroundTask 是一种通用任务,可计划在将来的某个日期使应用唤醒:The WKApplicationRefreshBackgroundTask is a generic Task that can be scheduled to have the app woken at a future date:

在任务的运行时中,应用可以执行任何类型的本地处理,例如更新复杂的时间线或使用 NSUrlSession获取一些必需的数据。Within the runtime of the Task, the app can do any kind of local processing such as update a Complication timeline or fetch some required data with a NSUrlSession.

WKURLSessionRefreshBackgroundTaskWKURLSessionRefreshBackgroundTask

当数据完成下载并准备好由应用程序处理时,系统将发送 WKURLSessionRefreshBackgroundTaskThe system will send a WKURLSessionRefreshBackgroundTask when the data has finished downloading and ready to be processed by the app:

在后台下载数据时,应用程序不会处于运行状态。An app is not left running while data is downloading in the background. 相反,应用程序会计划数据请求,然后将其挂起,系统将处理数据下载,仅在下载完成时 reawakening 应用。Instead, the app schedules the request for data, then it is suspended and the system handles the downloading of the data, only reawakening the app when the download is complete.

WKSnapshotRefreshBackgroundTaskWKSnapshotRefreshBackgroundTask

在 watchOS 3 中,Apple 添加了插接,用户可在其中固定喜爱的应用并对其进行快速访问。In watchOS 3, Apple has added the Dock where users can pin their favorite apps and quickly access them. 当用户按下 Apple Watch 上的侧按钮时,将显示固定应用程序快照的库。When the user presses the Side Button on the Apple Watch, a gallery of pinned app Snapshots will be displayed. 用户可以向左或向右轻扫以查找所需的应用程序,然后点击应用程序以启动它,并将快照替换为正在运行的应用程序的接口。The user can swipe left or right to find the desired app, then tap the app to launch it replacing the Snapshot with the running app's interface.

系统会定期拍摄应用程序的 UI (通过发送 WKSnapshotRefreshBackgroundTask)的快照,并使用这些快照来填充停靠。The system periodically takes snapshots of the app's UI (by sending a WKSnapshotRefreshBackgroundTask) and uses those snapshots to populate the Dock. watchOS 使应用有机会在拍摄此快照之前更新其内容和 UI。watchOS gives the app the opportunity to update its content and UI before this Snapshot is taken.

快照在 watchOS 3 中非常重要,因为它们充当应用程序的预览和启动映像。Snapshots are very important in watchOS 3 since they function as both the preview and launch images for the app. 如果用户在停靠中显示某个应用程序,它会扩展到全屏显示,进入前台并开始运行,因此,必须保持快照最新:If the user settles on an app in the Dock, it will expand to full screen, enter the foreground and start running, so it is imperative that the Snapshot be up-to-date:

同样,系统会发出 WKSnapshotRefreshBackgroundTask 以便在拍摄快照之前,应用程序可以准备(通过更新数据和 UI):Again, the system will issue a WKSnapshotRefreshBackgroundTask so that the app can prepare (by updating the data and the UI) before the snapshot is taken:

当应用将 WKSnapshotRefreshBackgroundTask 标记为 "已完成" 时,系统将自动拍摄应用 UI 的快照。When the app marks the WKSnapshotRefreshBackgroundTask completed, the system will automatically take a Snapshot of the app's UI.

重要

务必在应用收到新数据并更新其用户界面之后始终计划 WKSnapshotRefreshBackgroundTask,否则,用户将看不到修改后的信息。It is important to always schedule a WKSnapshotRefreshBackgroundTask after the app has received new data and updated its User Interface or the user will not see the modified information.

此外,当用户从应用程序收到通知并点击该通知以使应用程序进入前台时,快照必须是最新的,因为它也充当启动屏幕:Additionally, when the user receives a notification from the app and taps it to bring the app to the foreground, the Snapshot needs to be up-to-date since it is acting as the launch screen as well:

如果已超过一小时,因为用户已与 watchOS 应用交互,它将能够返回到其默认状态。If it has been more than one hour since the user has interacted with a watchOS app, it will be able to return to its Default State. 默认状态对于不同的应用程序可能有不同的含义,根据应用程序的设计,它可能根本没有默认状态。The Default State can mean different things to different apps and, based on the design of an app, it might not have a Default State at all.

WKWatchConnectivityRefreshBackgroundTaskWKWatchConnectivityRefreshBackgroundTask

在 watchOS 3 中,通过新的 WKWatchConnectivityRefreshBackgroundTask,Apple 已与后台刷新 API 集成了监视连接。In watchOS 3, Apple has integrated watch connectivity with the Background Refresh API via the new WKWatchConnectivityRefreshBackgroundTask. 使用这项新功能,iPhone 应用可以将最新的数据传递到其手表应用对应项,同时在后台运行 watchOS 应用程序:Using this new feature, an iPhone app can deliver fresh data to its watch app counterpart, while the watchOS app is running in the background:

启动复杂的推送、应用上下文、从 iPhone 应用发送文件或更新用户信息时,会在后台唤醒 Apple Watch 应用。Initiating a Complication Push, App Context, sending a file or updating User Information from the iPhone app will wake the Apple Watch app in the background.

当通过 WKWatchConnectivityRefreshBackgroundTask 来唤醒监视应用时,将需要使用标准 API 方法接收 iPhone 应用中的数据。When the watch app is woken via a WKWatchConnectivityRefreshBackgroundTask it will need to use the standard API methods to receive the data from the iPhone app.

  1. 确保会话已激活。Ensure that the Session has activated.
  2. 监视新的 HasContentPending 属性只要 true该值,应用仍有要处理的数据。Monitor the new HasContentPending property as long as the value is true, the app still has data to process. 与之前一样,应用程序应一直保存到任务,直到处理完所有数据为止。As before, the app should hold onto the Task until it has finished processing all data.
  3. 如果不再有要处理的数据(HasContentPending = false),请将任务标记为已完成以将其返回给系统。When there is no more data to be processed (HasContentPending = false), mark the Task completed to return it to the system. 如果无法执行此操作,将耗尽应用的分配后台运行时,从而导致崩溃报告。Failing to do this will exhaust the app's allotted background runtime resulting in a crash report.

后台 API 生命周期The Background API Lifecycle

将所有新的后台任务 API 放在一起,一组典型的交互如下所示:Placing all of the pieces of the new Background Tasks API together, a typical set of interactions would look like the following:

  1. 首先,watchOS 应用计划一个后台任务,使其在将来的某个时间点而唤醒于。First, the watchOS app schedules a background Task to be awoke as some point in the future.
  2. 此应用唤醒系统并发送任务。The app is woken by the system and sent a Task.
  3. 应用程序将处理任务以完成所需的任何工作。The app processes the Task to complete whatever work was required.
  4. 作为处理任务的结果,应用程序可能需要计划更多后台任务以在将来完成更多的工作,例如,使用 NSUrlSession下载更多内容。As a result of processing the Task, the app may need to schedule more background Tasks to complete more work in the future, such as downloading more content using a NSUrlSession.
  5. 应用程序将任务标记为已完成,并将其返回给系统。The app marks the Task completed and returns it to the system.

使用资源的责任Using Resources Responsibly

WatchOS 应用程序在此生态系统中的工作方式很重要,因为它限制了对系统共享资源的消耗。It is critical that a watchOS app behaves responsibly within this ecosystem by limiting its drain on the system's shared resources.

请查看以下方案:Take a look at the following scenario:

  1. 用户在下午1:00 启动 watchOS 应用。The user launches a watchOS app at 1:00 PM.
  2. 应用计划一个任务在下午2:00 的一小时内唤醒和下载新内容。The app schedules a task to wake up and download new content in an hour at 2:00 PM.
  3. 在下午1:50,用户可以重新打开应用程序,使其能够随时更新其数据和 UI。At 1:50 PM the user re-opens the app which allows it to update its data and UI at this time.
  4. 应用应重新计划任务,使其在晚于2:50 时运行一小时,而不是让任务在10分钟后再次唤醒应用。Instead of letting the Task wake the app again in 10 minutes, the app should reschedule the Task to run an hour later at 2:50 PM.

虽然每个应用程序都不同,但 Apple 建议查找使用模式(如上面所示的模式),以帮助节省系统资源。While every app is different, Apple suggests finding patterns of usage, like those shown above, to help conserve system resources.

实现后台任务Implementing Background Tasks

例如,本文档将使用向用户报告足球评分的虚假 MonkeySoccer 运动应用。For the sake of example, this document will use the fake MonkeySoccer sports app that reports soccer scores to the user.

请看下面的典型使用方案:Take a look at the following typical usage scenario:

用户的喜爱的足球团队正在播放从 7:00 PM 到 9:00 PM 的大匹配项,因此该应用程序应定期检查该分数,并决定30分钟的更新间隔。The user's favorite soccer team is playing a big match from 7:00 PM to 9:00 PM so the app should expect the user to be checking the score regularly and it decides on a 30 minute update interval.

  1. 用户将打开该应用程序,并在30分钟后计划一个用于后台更新的任务。The user opens the app and it schedules a Task for background update 30 minutes later. 后台 API 只允许在给定时间运行一种类型的后台任务。The Background API allows only one type of background Task to be running at a given time.
  2. 应用会接收该任务并更新其数据和 UI,并在30分钟后计划另一后台任务。The app receives the Task and updates its data and UI, then schedules for another background Task 30 minutes later. 开发人员必须记住计划另一后台任务,否则应用程序将不会重新唤醒以获取更多更新,这一点很重要。It is important that the developer remembers to schedule another background Task, or the app will never be re-awoken to get more updates.
  3. 同样,应用程序将接收该任务并更新其数据,更新其 UI,并在30分钟后计划另一后台任务。Again, the app receives the Task and updates its data, updates its UI and schedules another background Task 30 minutes later.
  4. 同一过程再次重复。The same process repeats again.
  5. 接收到最后一个后台任务,应用再次更新其数据和 UI。The last background Task is received and the app again updates its data and UI. 由于这是最终评分,因此不会计划新的后台刷新。Since this is the final score it doesn't schedule for a new background refresh.

计划后台更新Scheduling for Background Update

在上述方案中,MonkeySoccer 应用可以使用以下代码来计划后台更新:Given the above scenario, the MonkeySoccer app can use the following code to schedule for a background update:

private void ScheduleNextBackgroundUpdate ()
{
  // Create a fire date 30 minutes into the future
  var fireDate = NSDate.FromTimeIntervalSinceNow (30 * 60);

  // Create 
  var userInfo = new NSMutableDictionary ();
  userInfo.Add (new NSString ("LastActiveDate"), NSDate.FromTimeIntervalSinceNow(0));
  userInfo.Add (new NSString ("Reason"), new NSString ("UpdateScore"));

  // Schedule for update
  WKExtension.SharedExtension.ScheduleBackgroundRefresh (fireDate, userInfo, (error) => {
    // Was the Task successfully scheduled?
    if (error == null) {
      // Yes, handle if needed
    } else {
      // No, report error
    }
  });
}

当应用程序唤醒时,它将创建一个新的 NSDate 30 分钟的时间,并创建 NSMutableDictionary 来保存所请求任务的详细信息。It creates a new NSDate 30 minutes into the future when the app wants to be awoken and creates a NSMutableDictionary to hold the details of the requested Task. SharedExtensionScheduleBackgroundRefresh 方法用于请求计划任务。The ScheduleBackgroundRefresh method of the SharedExtension is used to request the task be scheduled.

如果系统无法计划请求的任务,系统将返回 NSErrorThe system will return a NSError if it was unable to schedule the requested Task.

处理更新Processing the Update

接下来,请仔细查看5分钟窗口,其中显示了更新评分所需的步骤:Next, take a closer look at the 5 minute window showing the steps required to update the score:

  1. 下午7:30:02,此应用程序会被系统唤醒,并提供更新后台任务。At 7:30:02 PM the app is awakened by the system and given the update background Task. 第一种优先级是从服务器获取最新分数。Its first priority is to get the latest scores from the server. 请参阅下面的计划 NSUrlSessionSee Scheduling a NSUrlSession below.
  2. 在7:30:05,应用完成了原始任务,系统会将应用置于睡眠状态,并继续在后台下载请求的数据。At 7:30:05 the app completes the original Task, the system puts the app to sleep and continues to download the requested data in the background.
  3. 当系统完成下载时,它将创建一个新任务来唤醒应用程序,以便它可以处理下载的信息。When the system completes the download, it creates a new Task to wake the app so it can process the downloaded information. 请参阅处理后台任务处理下面的下载See Handling Background Tasks and Handling the Download Completing below.
  4. 应用保存更新后的信息并将任务标记为已完成。The app saves the updated information and marks the Task completed. 开发人员目前可能会尝试更新应用程序的用户界面,但 Apple 建议计划快照任务来处理该过程。The developer may be tempted to update the app's User Interface at this time, however Apple suggests scheduling a Snapshot Task to handle that process. 请参阅下面的计划快照更新See Scheduling a Snapshot Update below.
  5. 应用会接收快照任务、更新其用户界面并将任务标记为已完成。The app receives the Snapshot Task, updates its User Interface and marks the Task completed. 请参阅下面的处理快照更新See Handling a Snapshot Update below.

计划 NSUrlSessionScheduling a NSUrlSession

可以使用以下代码来计划下载最新分数:The following code can be used to schedule the downloading of the latest scores:

private void ScheduleURLUpdateSession ()
{
  // Create new configuration
  var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration ("com.example.urlsession");

  // Create new session
  var backgroundSession = NSUrlSession.FromConfiguration (configuration);

  // Create and start download task
  var downloadTask = backgroundSession.CreateDownloadTask (new NSUrl ("https://example.com/gamexxx/currentScores.json"));
  downloadTask.Resume ();
}

它配置并创建新的 NSUrlSession,然后使用该会话通过 CreateDownloadTask 方法创建新的下载任务。It configures and creates a new NSUrlSession, then uses that session to create a new download Task using the CreateDownloadTask method. 它调用下载任务的 Resume 方法来启动会话。It calls the Resume method of the download Task to start the session.

处理后台任务Handling Background Tasks

通过重写 WKExtensionDelegateHandleBackgroundTasks 方法,应用程序可以处理传入的后台任务:By overriding the HandleBackgroundTasks method of the WKExtensionDelegate, the app can handle the incoming background tasks:

using System;
using System.Collections.Generic;
using Foundation;
using WatchKit;

namespace MonkeySoccer.MonkeySoccerExtension
{
  public class ExtensionDelegate : WKExtensionDelegate
  {
    #region Computed Properties
    public List<WKRefreshBackgroundTask> PendingTasks { get; set; } = new List<WKRefreshBackgroundTask> ();
    #endregion

    ...
    
    #region Public Methods
    public void CompleteTask (WKRefreshBackgroundTask task)
    {
      // Mark the task completed and remove from the collection
      task.SetTaskCompleted ();
      PendingTasks.Remove (task);
    }
    #endregion 

    #region Override Methods
    public override void HandleBackgroundTasks (NSSet<WKRefreshBackgroundTask> backgroundTasks)
    {
      // Handle background request
      foreach (WKRefreshBackgroundTask task in backgroundTasks) {
        // Is this a background session task?
        var urlTask = task as WKUrlSessionRefreshBackgroundTask;
        if (urlTask != null) {
          // Create new configuration
          var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration (urlTask.SessionIdentifier);

          // Create new session
          var backgroundSession = NSUrlSession.FromConfiguration (configuration, new BackgroundSessionDelegate (this, task), null);

          // Keep track of all pending tasks
          PendingTasks.Add (task);
        } else {
          // Ensure that all tasks are completed
          task.SetTaskCompleted ();
        }
      }
    }
    #endregion
    
    ...
  }
}

HandleBackgroundTasks 方法将遍历系统已发送应用的所有任务(在 backgroundTasks中)搜索 WKUrlSessionRefreshBackgroundTaskThe HandleBackgroundTasks method cycles through all of the Tasks that the system has sent the app (in backgroundTasks) searching for a WKUrlSessionRefreshBackgroundTask. 如果找到一个,则会重新加入会话并附加 NSUrlSessionDownloadDelegate 来处理下载完成(请参阅下面的处理下载完成):If one is found, it rejoins the session and attaches a NSUrlSessionDownloadDelegate to handle the download completing (see Handling the Download Completing below):

// Create new session
var backgroundSession = NSUrlSession.FromConfiguration (configuration, new BackgroundSessionDelegate (this, task), null);

它会在任务完成后,通过将其添加到集合来保留任务的句柄:It keeps a handle on the Task until it has completed by adding it to a collection:

public List<WKRefreshBackgroundTask> PendingTasks { get; set; } = new List<WKRefreshBackgroundTask> ();
...

// Keep track of all pending tasks
PendingTasks.Add (task);

所有发送到应用的任务都需要完成,对于当前未处理的任何任务,请将其标记为 "已完成":All of the Tasks sent to the app need to be completed, for any task not currently being handled, mark it complete:

if (urlTask != null) {
  ...
} else {
  // Ensure that all tasks are completed
  task.SetTaskCompleted ();
}

正在处理下载完成Handling the Download Completing

MonkeySoccer 应用使用以下 NSUrlSessionDownloadDelegate 委托处理完成的下载并处理请求的数据:The MonkeySoccer app uses the following NSUrlSessionDownloadDelegate delegate to handle the download completing and process the requested data:

using System;
using Foundation;
using WatchKit;

namespace MonkeySoccer.MonkeySoccerExtension
{
  public class BackgroundSessionDelegate : NSUrlSessionDownloadDelegate
  {
    #region Computed Properties
    public ExtensionDelegate WatchExtensionDelegate { get; set; }

    public WKRefreshBackgroundTask Task { get; set; }
    #endregion

    #region Constructors
    public BackgroundSessionDelegate (ExtensionDelegate extensionDelegate, WKRefreshBackgroundTask task)
    {
      // Initialize
      this.WatchExtensionDelegate = extensionDelegate;
      this.Task = task;
    }
    #endregion

    #region Override Methods
    public override void DidFinishDownloading (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
    {
      // Handle the downloaded data
      ...

      // Mark the task completed
      WatchExtensionDelegate.CompleteTask (Task);

    }
    #endregion
  }
}

初始化后,它会保留 ExtensionDelegate 的句柄和生成它的 WKRefreshBackgroundTaskWhen initialized, it keeps a handle to both the ExtensionDelegate and the WKRefreshBackgroundTask that spawned it. 它重写 DidFinishDownloading 方法来处理下载完成。It overrides the DidFinishDownloading method to handle the download completing. 然后使用 ExtensionDelegateCompleteTask 方法来通知任务已完成,并将其从挂起任务的集合中删除。Then uses the CompleteTask method of the ExtensionDelegate to inform the Task that it has completed and remove it from the collection of pending tasks. 请参阅处理上述后台任务See Handling Background Tasks above.

计划快照更新Scheduling a Snapshot Update

下面的代码可用于计划快照任务,以使用最新分数更新 UI:The following code can be used to schedule a Snapshot Task to update the UI with the latest scores:

private void ScheduleSnapshotUpdate ()
{
  // Create a fire date of now
  var fireDate = NSDate.FromTimeIntervalSinceNow (0);

  // Create user info dictionary
  var userInfo = new NSMutableDictionary ();
  userInfo.Add (new NSString ("lastActiveDate"), NSDate.FromTimeIntervalSinceNow (0));
  userInfo.Add (new NSString ("reason"), new NSString ("UpdateScore"));

  // Schedule for update
  WKExtension.SharedExtension.ScheduleSnapshotRefresh (fireDate, userInfo, (error) => {
    // Was the Task successfully scheduled?
    if (error == null) {
      // Yes, handle if needed
    } else {
      // No, report error
    }
  });
}

就像上述 ScheduleURLUpdateSession 方法一样,它会创建一个新的 NSDate,以便在应用程序唤醒时,创建一个 NSMutableDictionary 来保存所请求任务的详细信息。Just like ScheduleURLUpdateSession method above, it creates a new NSDate for when the app wants to be awoken and creates a NSMutableDictionary to hold the details of the requested Task. SharedExtensionScheduleSnapshotRefresh 方法用于请求计划任务。The ScheduleSnapshotRefresh method of the SharedExtension is used to request the task be scheduled.

如果系统无法计划请求的任务,系统将返回 NSErrorThe system will return a NSError if it was unable to schedule the requested Task.

处理快照更新Handling a Snapshot Update

为了处理快照任务,将修改 HandleBackgroundTasks 方法(请参阅处理上述后台任务),如下所示:To handle the Snapshot Task, the HandleBackgroundTasks method (see Handling Background Tasks above) is modified to look like the following:

public override void HandleBackgroundTasks (NSSet<WKRefreshBackgroundTask> backgroundTasks)
{
  // Handle background request
  foreach (WKRefreshBackgroundTask task in backgroundTasks) {
    // Take action based on task type
    if (task is WKUrlSessionRefreshBackgroundTask) {
      var urlTask = task as WKUrlSessionRefreshBackgroundTask;

      // Create new configuration
      var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration (urlTask.SessionIdentifier);

      // Create new session
      var backgroundSession = NSUrlSession.FromConfiguration (configuration, new BackgroundSessionDelegate (this, task), null);

      // Keep track of all pending tasks
      PendingTasks.Add (task);
    } else if (task is WKSnapshotRefreshBackgroundTask) {
      var snapshotTask = task as WKSnapshotRefreshBackgroundTask;

      // Update UI
      ...

      // Create a expiration date 30 minutes into the future
      var expirationDate = NSDate.FromTimeIntervalSinceNow (30 * 60);

      // Create user info dictionary
      var userInfo = new NSMutableDictionary ();
      userInfo.Add (new NSString ("lastActiveDate"), NSDate.FromTimeIntervalSinceNow (0));
      userInfo.Add (new NSString ("reason"), new NSString ("UpdateScore"));

      // Mark task complete
      snapshotTask.SetTaskCompleted (false, expirationDate, userInfo);
    } else {
      // Ensure that all tasks are completed
      task.SetTaskCompleted ();
    }
  }
}

方法测试正在处理的任务的类型。The method tests for the type of Task being processed. 如果是 WKSnapshotRefreshBackgroundTask 它将获得对任务的访问权限:If it is a WKSnapshotRefreshBackgroundTask it gains access to the task:

var snapshotTask = task as WKSnapshotRefreshBackgroundTask;

方法更新用户界面,然后创建一个 NSDate 来告知系统快照将过时。The method updates the User Interface then creates a NSDate to tell the system when the Snapshot will be stale. 它使用用户信息创建 NSMutableDictionary,用于描述新的快照,并使用此信息将快照任务标记为已完成:It creates a NSMutableDictionary with user info to describe the new Snapshot and marks the Snapshot Task completed with this information:

// Mark task complete
snapshotTask.SetTaskCompleted (false, expirationDate, userInfo);

此外,它还告诉快照任务应用程序未返回到默认状态(在第一个参数中)。Additionally, it also tells the Snapshot Task that the app is not returning to the Default State (in the first parameter). 没有默认状态概念的应用应始终将此属性设置为 trueApps that have no concept of a Default State should always set this property to true.

有效地工作Working Efficiently

如前面的示例中所示,MonkeySoccer 应用程序用来更新其评分,通过高效地工作并使用新的 watchOS 3 后台任务,该应用只需15秒的活动状态:As seen in the above example of the five minute window that the MonkeySoccer app took to update its scores, by working efficiently and using the new watchOS 3 background Tasks, the app was only active for a total of 15 seconds:

这会降低应用对可用 Apple Watch 资源和电池寿命的影响,还可让应用更好地与在手表上运行的其他应用程序一起工作。This lowers the impact that the app will have on both available Apple Watch resources and battery life and also allows the app to work better with other apps running on the watch.

计划的工作方式How Scheduling Works

当 watchOS 3 应用处于前台时,它将始终计划运行,并可执行所需的任何类型的处理(如更新数据或重绘其 UI)。While a watchOS 3 app is in the foreground, it is always scheduled to run and can do any type of processing required such as update data or redraw its UI. 当应用移到后台时,它通常被系统挂起,所有运行时操作都将暂停。When the app moves into the background, it is usually suspended by the system and all runtime operations are halted.

当应用处于后台时,系统可能会将其设定为目标,以快速运行特定的任务。While the app is in the background, it may be targeted by the system to quickly run a specific task. 因此,在 watchOS 2 中,系统可能会暂时唤醒后台应用程序以执行操作,例如处理长时间通知或更新应用程序的问题。So, in watchOS 2, the system might temporarily wake a background app to do things like handling a long look notification or to update the app's Complication. 在 watchOS 3 中,有几种新方法可在后台运行应用。In watchOS 3, there are several new ways that an app can be run in the background.

当应用处于后台时,系统对其施加了几个限制:While an app is in the background, the system imposes several limits on it:

  • 只有几秒钟时间才能完成任何给定的任务。It is only given a few seconds to complete any given task. 系统不仅要考虑所经过的时间量,还需要考虑应用程序使用多少 CPU 能力来派生此限制。The system takes into consideration not only the amount of time passed but also how much CPU power the app is consuming to derive this limit.
  • 超出其限制的任何应用都将终止,并出现以下错误代码:Any app that exceeds its limits will be killed with the following error codes:
    • CPU -0xc51bad01CPU - 0xc51bad01
    • 时间-0xc51bad02Time - 0xc51bad02
  • 系统会根据要求应用执行的后台任务类型来施加不同的限制。The system will impose different limits based on the type of Background Task it has asked the app to perform. 例如,通过其他类型的后台任务对 WKApplicationRefreshBackgroundTaskWKURLSessionRefreshBackgroundTask 任务提供稍长的运行时。For example, WKApplicationRefreshBackgroundTask and WKURLSessionRefreshBackgroundTask Tasks are given slightly longer runtimes over other types of Background Tasks.

复杂和应用更新Complications and App Updates

除了 Apple 添加到 watchOS 3 的新后台任务外,watchOS 应用的复杂程度还会影响应用接收后台更新的方式和时间。In addition to the new Background Tasks that Apple has added to watchOS 3, a watchOS app's Complications can have an affect on how and when the app receives background updates.

复杂的是提供有用信息的小视觉元素。Complications are small visual elements that provide useful information at a glance. 根据所选的监视面,用户可以自定义一个或多个会在 watchOS 3 中的监视应用程序中提供的一个或多个复杂的监视面。Depending on the watch face selected, the user has the ability to customize a watch face with one or more Complication that can be supplied by a watch app in watchOS 3.

如果用户在其手表面上包含某个应用的复杂问题,则会为应用提供以下更新的优点:If the user includes one of the app's Complications on their watch face, it gives the app the following updated benefits:

  • 它会导致系统使应用程序处于随时可用状态,在该状态下,它会尝试在后台启动应用程序,将其保存在内存中,并提供额外的更新时间。It causes the system to keep the app in a ready-to-launch state, where it attempts to launch the app in the background, keeps it in memory and gives it extra time to update.
  • 最复杂的一天保证至少50推送更新。Complications are guaranteed at least 50 push updates per day.

开发人员应始终努力为其应用程序带来引人注目的复杂性,以使用户能够根据上面列出的原因将其添加到其手表中。The developer should always strive to create compelling Complications for their apps to entice the user into adding them to their watch face for the reasons listed above.

在 watchOS 2 中,这是应用在后台收到运行时的主要方式。In watchOS 2, Complications were the primary way that an app received runtime while in the background. 在 watchOS 3 中,仍将确保一项复杂的应用每小时接收多个更新,但它可以使用 WKExtensions 来请求更多运行时,以更新其复杂性。In watchOS 3, a Complication app will still be ensured to receive multiple updates per hour, however, it can use WKExtensions to request more runtime to update its complications.

请查看以下用于更新连接的 iPhone 应用中的难点的代码:Take a look at the following code used to update the Complication from the connected iPhone app:

using System;
using WatchConnectivity;
using UIKit;
using Foundation;
using System.Collections.Generic;
using System.Linq;
...

private void UpdateComplication ()
{

  // Get session and the number of remaining transfers
  var session = WCSession.DefaultSession;
  var transfers = session.RemainingComplicationUserInfoTransfers;

  // Create user info dictionary
  var iconattrs = new Dictionary<NSString, NSObject>
    {
      {new NSString ("lastActiveDate"), NSDate.FromTimeIntervalSinceNow (0)},
      {new NSString ("reason"), new NSString ("UpdateScore")}
    };

  var userInfo = NSDictionary<NSString, NSObject>.FromObjectsAndKeys (iconattrs.Values.ToArray (), iconattrs.Keys.ToArray ());

  // Take action based on the number of transfers left
  if (transfers < 1) {
    // No transfers left, either attempt to send or inform
    // user of situation.
    ...
  } else if (transfers < 11) {
    // Running low on transfers, only send on important updates
    // else conserve for a significant change.
    ...
  } else {
    // Send data
    session.TransferCurrentComplicationUserInfo (userInfo);
  }
}

它使用 WCSessionRemainingComplicationUserInfoTransfers 属性来查看应用在一天中剩余的高优先级传输数,然后根据该数字采取措施。It uses the RemainingComplicationUserInfoTransfers property of the WCSession to see how many high priority transfers the app has left for the day and then takes action based on that number. 如果应用程序的传输时间不足,则它可以在发送次要更新时保持不变,并且仅在发生重大更改时发送信息。If the app begins to run low on transfers, it can hold off on sending minor updates and only send information when there is a significant change.

计划和停靠Scheduling and the Dock

在 watchOS 3 中,Apple 添加了插接,用户可在其中固定喜爱的应用并对其进行快速访问。In watchOS 3, Apple has added the Dock where users can pin their favorite apps and quickly access them. 当用户按下 Apple Watch 上的侧按钮时,将显示固定应用程序快照的库。When the user presses the Side Button on the Apple Watch, a gallery of pinned app snapshots will be displayed. 用户可以向左或向右轻扫以查找所需的应用程序,然后点击应用程序以启动它,并将快照替换为正在运行的应用程序的接口。The user can swipe left or right to find the desired app, then tap the app to launch it replacing the snapshot with the running app's interface.

系统会定期拍摄应用 UI 的快照,并使用这些快照来填充文档。watchOS 使应用有机会在拍摄此快照之前更新其内容和 UI。The system periodically takes snapshots of the app's UI and uses those snapshots to populate the Docs. watchOS gives the app the opportunity to update its content and UI before this snapshot is taken.

已固定到停靠的应用可能需要以下各项:Apps that have been pinned to the dock can expect the following:

  • 它们最少每小时更新一次。They will receive a minimum of one updated per hour. 这包括应用刷新任务和快照任务。This includes both an App Refresh Task and a Snapshot Task.
  • 更新预算在停靠中的所有应用之间分布。The update budget is distributed between all of the apps in the Dock. 因此,用户所固定的应用越少,每个应用接收的潜在更新就越多。So the fewer apps the user has pinned, the more potential updates each app will receive.
  • 该应用程序将保留在内存中,以便在从插接中选择应用程序时,应用程序将会快速恢复。The app will be kept in memory so the app will resume quickly when selected from the Dock.

用户运行的最后一个应用将被视为_最近使用_的应用,并且将占据停靠中的最后一个槽。The last app the user ran will be considered the Most Recently Used app and will occupy the last slot in the Dock. 用户可以选择将其永久固定到停靠。From there, there user can choose to pin it permanently to the Dock. 最近使用的将视为用户已固定到停靠的任何其他最喜爱的应用。The Most Recently Used will be treated like any other favorite app the user has already pinned to the Dock.

重要

只有添加到主屏幕的应用才会获得定期计划。Apps that have only been added to the Home Screen will not be given any regular scheduling. 若要接收定期计划和后台更新,_必须_将应用添加到停靠。To receive regular scheduling and background updates, an app must be added to the Dock.

如本文档前面所述,快照在 watchOS 3 中非常重要,因为它们充当应用程序的预览和启动映像。As stated earlier in this document, Snapshots are very important in watchOS 3 since they function as both the preview and launch images for the app. 如果用户在停靠中显示某个应用程序,它会扩展到全屏显示,进入前台并开始运行,因此,必须保持快照最新状态。If the user settles on an app in the Dock, it will expand to full screen, enter the foreground and start running, so it is imperative that the Snapshot be up-to-date.

有时,系统可能会确定它需要应用 UI 的新快照。There might be times when the system decides it needs a fresh Snapshot of the app's UI. 在这种情况下,快照请求不会对应用的运行时预算计数。In this situations, the Snapshot request will not count against the app's runtime budget. 以下操作将触发系统快照请求:The following will trigger a system Snapshot request:

  • 较复杂的时间线更新。A Complication timeline update.
  • 用户与应用程序的通知交互。User interaction with an app's notification.
  • 从前台切换到后台状态。Switching from the Foreground to the Background state.
  • 在后台状态一小时后,应用可以恢复为默认状态。After one hour of being in the Background state, so the app can return to the Default State.
  • 首次启动 watchOS 时。When watchOS first boots.

最佳做法Best Practices

Apple 建议在使用后台任务时采用以下最佳做法:Apple suggests the following best practices when working with Background Tasks:

  • 在应用需要更新时按计划进行。Schedule as often as the app needs to be updated. 每次应用运行时,它应该重新评估其未来需求,并根据需要调整此计划。Every time the app runs it should re-evaluate its future needs and adjust this schedule as required.
  • 如果系统发送后台刷新任务,且应用程序不需要更新,请推迟工作,直到实际需要更新。If the system sends a Background Refresh Task and the app doesn't require an update, defer the work until an update is actually required.
  • 考虑可用于应用的所有运行时机会:Consider all runtime opportunities available to an app:
    • 停靠和前台激活。Dock and Foreground activation.
    • 报警.Notifications.
    • 更新。Complication updates.
    • 后台刷新。Background refreshes.
  • 使用 ScheduleBackgroundRefresh 进行一般用途的后台运行时,例如:Use ScheduleBackgroundRefresh for general-purpose background runtime such as:
    • 轮询系统以获取信息。Polling the system for information.
    • 计划未来 NSURLSessions 请求背景数据。Schedule future NSURLSessions to request background data.
    • 已知时间转换。Known time transitions.
    • 触发复杂的更新。Triggering Complication updates.

快照最佳实践Snapshot Best Practices

使用快照更新时,Apple 提出以下建议:When working with Snapshot updates, Apple makes the following suggestions:

  • 仅在需要时(例如,在有重大内容更改时)使快照无效。Invalidate Snapshots only when required, for example, when there is a significant content change.
  • 避免高频率快照失效。Avoid high-frequency Snapshot invalidation. 例如,计时器应用不应每秒更新一次快照,只应在计时器结束时执行。For example, a timer app shouldn't update the Snapshot every second, it should only be done when the timer has ended.

应用数据流App Data Flow

Apple 建议使用以下项来处理数据流:Apple suggest the following for working with data flow:

外部事件(如手表连接)唤醒应用。An external event (such as Watch Connectivity) wakes the app. 这会强制应用程序更新其数据模型(表示应用程序的当前状态)。This forces the app to update its Data Model (that represents the apps current state). 由于数据模型更改,应用将需要更新其复杂性,请求新的快照,可能会启动一个后台 NSURLSession 来请求更多的数据和计划进一步的后台刷新。As a result of the Data Model change the app will need to update its Complications, request a new Snapshot, possibly start a background NSURLSession to pull more data and schedule further background refreshes.

应用生命周期The App Lifecycle

由于可以将收藏的应用固定到一起,因此 Apple 认为用户将在更多的应用之间移动,更常见的是,它们都是 watchOS 2。Because of the Dock and the ability to pin favorite apps to it, Apple thinks that users will be moving between far more apps, far more often, then they did with watchOS 2. 因此,应用应准备好处理此更改,并快速在前台和后台状态之间移动。As a result, the app should be ready to handle this change and move between the foreground and background states quickly.

Apple 提供以下建议:Apple has the following suggestions:

  • 请确保应用程序在进入前台激活时尽快完成所有后台任务。Ensure that the app finishes any background task as soon as possible upon entering foreground activation.
  • 请确保在通过调用 NSProcessInfo.PerformExpiringActivity进入背景之前完成所有前景工作。Ensure to finish all foreground work before entering the background by calling NSProcessInfo.PerformExpiringActivity.
  • 在 watchOS 模拟器中测试应用时,不会强制执行任何任务预算,因此应用可以尽可能多地刷新,以正确地测试功能。When testing an app in the watchOS Simulator, none of the Task budgets will be enforced so an app can refresh as much as needed to properly test a feature.
  • 请始终在实际 Apple Watch 硬件上进行测试,以确保应用在发布到 iTunes Connect 之前不会在其预算之外运行。Always test on real Apple Watch hardware to ensure that the app isn't running past its budgets before publishing to iTunes Connect.
  • Apple 建议在测试和调试时在充电器上保留 Apple Watch。Apple suggests keeping the Apple Watch on the charger while testing and debugging.
  • 请确保完全测试了冷启动和恢复应用。Ensure that both cold launching and resuming an app are thoroughly tested.
  • 验证所有应用任务是否已完成。Verify that all app Tasks are being completed.
  • 更改停靠中固定的应用数量,以测试最佳和最差情况。Vary the number of apps that are pinned in the Dock to test both the best and worst case scenarios.

总结Summary

本文介绍了 Apple 对 watchOS 的增强功能,以及如何使用它们来使手表应用保持最新状态。This article has covered the enhancements Apple has made to watchOS and how they can be used to keep a watch app up-to-date. 首先,它涵盖了 Apple 添加到 watchOS 3 中的所有新后台任务。First, it covered all of the new Background Task Apple has added in watchOS 3. 然后,它介绍了后台 API 生命周期,以及如何实现 Xamarin watchOS 应用程序中的后台任务。Then, it covered the Background API Lifecycle and how to implement Background Tasks in a Xamarin watchOS app. 最后,它介绍了计划的工作方式,并提供了一些最佳实践。Finally, it covered how scheduling works and gave some best practices.