Опубликован: 15.05.2013 | Доступ: свободный | Студентов: 265 / 9 | Длительность: 24:25:00
Специальности: Системный архитектор
Лекция 6:

Push-уведомления и Windows Push Notification Service

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >

Создание и регистрация фоновой задачи

Объявление фоновой задачи в манифесте – это лишь объявление, которое сообщает системе о том, что приложение намеревается использовать фоновую задачу. Приложение должно зарегистрировать фоновую задачу из кода для того, чтобы она могла исполняться, что реализуется с помощью Windows.ApplicationModel.Background.BackgroundTaskBuilder (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.backgroundtaskbuilder.aspx) (его родительское пространство имен содержит всё, к чему мы будем обращаться в контексте фоновых задач). Проще говоря, вы создаете экземпляр средства для создания фоновой задачи, задаёте его свойства name и taskEntryPoint, вызываете его методы setTrigger и addCondition для указания того, когда следует запускать фоновую задачу, и затем вызываете register.

Обычный код для этого можно найти в примере "Фоновая задача" (http://code.msdn.microsoft.com/windowsapps/Background-Task-Sample-9209ade9), в js/global.js.

Этот модуль объявляет глобальный объект BackgroundTaskSample, который содержит свойства и методы. Нас здесь интересует метод, который называется registerBackgroundTask, он регистрирует заданную точку входа (имя JavaScript-файла или имя класса в C#, Visual Basic, или C++), с заданным именем и применяет некоторые триггеры и условия.

var BackgroundTaskSample = {
// Свойства, содержащие имена и точки входа для задач примера опущены

//
// Регистрация фоновой задачи с заданными taskEntryPoint, taskName, trigger,
// и condition (дополнительно).
//
"registerBackgroundTask": function (taskEntryPoint, taskName, trigger, condition) {
var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();

builder.name = taskName; builder.taskEntryPoint = taskEntryPoint; builder.setTrigger(trigger);

if (condition !== null) {
builder.addCondition(condition);
}

var task = builder.register(); BackgroundTaskSample.attachProgressAndCompletedHandlers(task);

// [Опущен код, отражающий особенности примера]

// Удаление предыдущего статуса завершения из локальных параметров.
var settings = Windows.Storage.ApplicationData.current.localSettings;
settings.values.remove(taskName);
},

В этом коде метод BackgroundTaskBuilder.register возвращает объект BackgroundTaskRegistration (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.backgroundtaskregistration.aspx), с помощью которого управляют зарегистрированной задачей. У зарегистрированной задачи есть свойство name и назначенное системой свойство taskId, последнее из них вы можете использовать для сохранения данных приложения, которые уникальны для задачи. Так же у него есть метод unregister, который вы можете вызвать для отмены регистрации, и два события: completed и progress. Обработчики для этих событий назначают обычным способом, с помощью addEventListener, как показано в функции BackgroundTaskSample.attachProgressAndCompletedHandlers в примере:

"attachProgressAndCompletedHandlers": function (task) {
 task.addEventListener("progress",
 new BackgroundTaskSample.progressHandler(task).onProgress);
 task.addEventListener("completed",
 new BackgroundTaskSample.completeHandler(task).onCompleted);
 },

Одна из основных целей использования этих обработчиков заключается в выполнении задач обновления пользовательского интерфейса в ответ на данные, оставленные фоновой задачей. Эти задачи, сами по себе, не могут работать с пользовательским интерфейсом, но они могут сохранять данные в областях данных приложения, доступ к которым может получить и основное приложение. События completed и progress, таким образом, являются тем, с помощью чего основное приложение – то, которое может работать с пользовательским интерфейсом – может отреагировать на эти события фоновых задач для того, чтобы прочитать информацию из областей данных приложения и выполнить необходимые обновления. Пример "Фоновая задача" (http://code.msdn.microsoft.com/windowsapps/Background-Task-Sample-9209ade9) выполняет это в каждом из своих сценариев.

Здесь присутствует одно статическое свойство, BackgroundTaskRegistration.allTasks, объект IMapView , с помощью которого вы можете получить свои зарегистрированные задачи и получить для каждого отдельный объект BackgroundTaskRegistration.

Очень важно отметить, что Windows позволяет регистрировать одну и ту же фоновую задачу дважды и присваивает уникальные значения taskId каждой из них, поэтому будьте осторожны, чтобы не столкнуться с дублированием задач. Обратите так же внимание на то, как вышеприведенный код registerBackgroundTask пользуется контейнером локальных параметров в данных приложения. Это – способ связи приложения с каждой из своих независимых задач, так как данные приложения – это единственное средство для обмена подобными данными.

Имея подобную структуру, мы можем задаться вопросом: что мы делаем для настройки триггеров и условий? Ответ на этот вопрос – это то, что различает разные виды фоновых задач.

Условия

Конкретные условия вы можете задать с помощью объекта BackgroundTaskBuilder.addCondition (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.backgroundtaskbuilder.addcondition.aspx), который является экземпляром класса SystemCondition (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.systemcondition.aspx). Фоновая задача, зарегистрированная с одним или большим количеством условий – каждый вызов addCondition приводит к добавлению очередного условия – будет запланирована к выполнению только если все условия выполняются.

Каждый экземпляр может иметь один из следующих типов, заданных в перечислении SystemConditionType: internetAvailable (интернет-подключение доступно), internetNotAvailable (интернет-подключение не доступно), sessionConnected (пользователь вошёл в систему), sessionDisconnected (пользователь вышел из системы), userPresent (пользователь в настоящий момент занят какими-то делами), и userNotPresent (пользователь ничего не делает некоторое время).

Очевидно, каждая пара из этих условий является взаимоисключающей: если вы зарегистрировали задачу с условиями internetAvailable и internetNotAvailable, Windows поймёт, что вы никогда не планируете, чтобы эта задача когда-нибудь запускалась, поэтому система оставит её сидеть на обочине и никогда не побеспокоит! С другой стороны, вы можете использовать эти условия для того, чтобы быть уверенным, что ваша задача выполняется только тогда, когда нужна. Если вам нужно запустить фоновую задачу, которая обновляет канал push-уведомлений, например, нет смысла этого делать при отсутствии подключения к Интернету. (В следующем разделе, "Задачи для триггеров обслуживания", мы увидим пример). С другой стороны, если у вас есть фоновая задача, исполнение которой никогда не должно совпадать с активной работой пользователя, вы можете добавить условие userNotPresent.

Обратите внимание на то, что так как условие sessionDisconnected выполняется тогда, когда пользователь выходит из системы, оно полезно лишь для фоновых задач, которые используют экран блокировки.

Задачи для триггеров обслуживания

Фоновые задачи, использующие триггеры обслуживания, вероятно, являются наиболее общим типом задач – они содержат какой-либо код, который нужно периодически запускать, но только тогда, когда система подключена к источнику питания . Подобные задачи отлично подхдят для "проверки на что-либо", или для другой деятельности, которую нужно выполнять периодически, но при этом не важно – когда точно это произойдёт. Например, триггеры обслуживания не подходят для чего-то вроде синхронизации данных с сервером, так как это должно происходить своевременно, и это лучше будет реализовать с помощью API фоновой передачи данных, которое мы рассмотрим в Главе 3.

Триггер обслуживания – то, что вы передаете в BackgroundTaskBuilder.setTrigger— это экземпляр класса MaintenanceTrigger (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.maintenancetrigger.aspx). При создании экземпляра предоставляют два параметра. Первый – это необходимый период обновления (свойство freshnessTime, в минутах), всегда следует использовать наибольший период, подходящий для конкретного сценария. Система будет всегда ожидать, как минимум, прохождения этого периода, перед первым запуском задачи. Второй параметр – это флаг, который указывает на то, нужно ли запустить задачу лишь один раз (свойство oneShot).

Сценарий 2 примера Push-уведомления и периодические уведомления, клиентская часть" (http://code.msdn.microsoft.com/windowsapps/Push-and-periodic-de225603) показывает использование триггера обслуживания для периодического обновления WNS-канала, как описано ранее в разделе "Запрос и кэширование UIR канала". Этот код взят из js/scenario2.js, некоторая его часть находится во внутренней функции, которая называется registerTask:

var background = Windows.ApplicationModel.Background;
  var pushNotificationsTaskName = "UpdateChannels";
  var maintenanceInterval = 10 * 24 * 60; // 10 дней

  var taskBuilder = new background.BackgroundTaskBuilder();
  var trigger = new background.MaintenanceTrigger(maintenanceInterval, false);
  taskBuilder.setTrigger(trigger);
  taskBuilder.taskEntryPoint = "js\\backgroundTask.js";
  taskBuilder.name = pushNotificationsTaskName;

  var internetCondition = new background.SystemCondition(background.SystemConditionType.internetAvailable);
  taskBuilder.addCondition(internetCondition);

  taskBuilder.register();

Так как срок действия URI канала – 30 дней, пример создаёт триггер, предназначенный для исполнения каждые 10 дней (10 дней * 24 часа в день * 60 минут в часе). Так же сюда разумно добавлено условие internetAvailable, так как не имеет смысла пытаться обновить URI канала, когда нет подключения к Интернету.

Саму задачу можно найти в файле примера js/backgroundTask.js, что отражено в свойстве taskEntryPoint:

(function () {
// Импорт вспомогательного объекта Notifier
importScripts("//Microsoft.WinJS.1.0/js/base.js");
importScripts("notifications.js");

var closeFunction = function () {
close();
};

var notifier = new SampleNotifications.Notifier();
notifier.renewAllAsync().done(closeFunction, closeFunction);
})();

Код задачи использует пару других скриптовых файла, importScripts, второй из которых, notifications.js, это набор вспомогательных функций образца для уведомлений, где renewAllAsync обновляет список, в котором находятся URI канала, ранее сохраненные приложением.

Важно. Вы так же можете заметить, что обработчики завершения и ошибки задаваемые в promise-объекте из функции renewAllAsync , оба ведут в функцию closeFunction, которая выполняет таинственный вызов close. Что это за close? Так, это не window.close (как вы могли предоложить). Это WorkerGlobalScope.close ( http://www.w3.org/TR/workers/#workerglobalscope). Фоновые задачи в приложении, написанном на JavaScript, запускаются в областе видимости рабочего веб-процесса, таким образом, глобальная область в этом коде – это WorkerGlobalScope, а не window. Данный вызов позволяет убедиться в закрытии независимо работающей фоновой задачи и гарантирует, что ресурсы, которые были выделены для задачи, соответствующим образом освобождены.

Врезка: Экземпляр задачи и острочка фоновой задачи

В фоновой задаче JavaScript, Windows.UI.WebUI.WebUIBackgroundTaskInstance.current содержит объект WebUIBackgroundTaskInstanceRuntimeClass (http://msdn.microsoft.com/library/windows/apps/windows.ui.webui.webuibackgroundtaskinstanceruntimeclass.aspx), который предоставляет дополнительные сведения о выполняющейся задаче: её instanceId, связанный с ней объект BackgroundTaskRegistration в свойстве task, свойство progress, в котором задача может сохранить процентное значение степени её выполнения, флаг succeeded , который позволяет указать, что задача завершена, свойство suspendedСount (показывает, сколько раз был приостановлена задача при превышении ограничений), и событие canceled , которое сообщает о том, что задача будет закрыта.

Этот объект так же предоставляет метод getDeferral , который возвращает отложенный объект, метод completed которого вызывается, когда задача завершена. Как и всегда, вы используете откладывание операции, если вам нужно выполнить асинхронную операцию в фоновой задаче. Не забудьте всегда вызывать close, когда все действия будут выполнены.

Задачи для системных триггеров (не для экрана блокировки)

Следующий класс фоновых задача содержит следующие привязки к различным системным триггерам, в частности, экземпляры класса SystemTrigger (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.background.systemtrigger.aspx). Объект триггера создают с помощью ключевого слова new и передают два параметра: значение SystemTriggerType value (доступное потом как свойство triggerType) и флаг логического типа oneShot. Эти триггеры могут работать независимо от экрана блокировки, они описаны в следующей таблице:

SystemTriggerType Когда вызывается и сценарии использования
internetAvailable Стало доступно подключение к Интернету. Это обычно используется для приложений, которым нужно запустить процесс синхронизации, когда восстанавливается подключение к Интернету. Обратите внимание на то, что этот триггер отличается от условия с тем же самым именем.
lockScreenApplicationAdded Пользователь добавил прилоежение на экран блокировки, что указывает на то, что фоновые задачи, зависимые от экрана блокировки теперь будут исполняться.
lockScreenApplicationRemoved Пользователь удалил приложение с экрана блокировки, что указывает на то, что фоновые задачи, зависимые от экрана блокировки, теперь исполняться не будут.
networkStateChange Изменение сетевых условий (стоимости, типа подключения и так далее). Исполняющееся приложение может обнаружить то же самое с помощью события Windows.Networking.Connectivity.NetworkInformation.onnetworkstatuschanged, он предоставляет приложению возможность исполнить небольшой фрагмент кода, когда приложение приостановлено или не исполняется. Этот триггер, в комбинации с условиями internetAvailable или internetNotAvailable позволяет приложению быть полностью осведомленным о состоянии подключения. Мы погвоорим об этом в Главе 3, сейчас вы можете обратиться к примеру "Фоновое определение состояния сети" (http://code.msdn.microsoft.com/windowsapps/Network-status-background-957eb3eb).
onlineIdConnectedStateChange Учетная запись Microsoft пользователя изменена. Это сравнительно редкое событие, но триггер является основным средством для любых приложений, которые кэшируют любые части сведений из учетной записи Microsoft для собственных целей идентификации пользователя.
servicingComplete Завершено бновление приложения из Магазина Windows. Этот триггер можно использовать, например, для выполнения миграции данных приложения с одной версии на другую как только произойдёт обновлении. Подробнее о версиях данных приложения вы можете узнать в Главе 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript".
timeZoneChange Изменен часовой пояс или осуществлен переход на летнее или зимнее время. Приложение в этот момент может обновить данные о географическом местоположении, настроить внутренние механизмы, связанные с учетом времени. Это может быть важно для настройки запланированных уведомлений.

В примере "Фоновая задача" есть несколько примеров этих триггеров. В Сценарии 1, js/sample-background-task-with-condition.js, мы можем видеть использование триггера timeZoneChange и условия userPresent (здесь BackgroundTaskSample это вспомогательный объект из global.js):

BackgroundTaskSample.registerBackgroundTask(BackgroundTaskSample.sampleBackgroundTaskEntryPoint,
  BackgroundTaskSample.sampleBackgroundTaskWithConditionName,
  new Windows.ApplicationModel.Background.SystemTrigger(
  Windows.ApplicationModel.Background.SystemTriggerType.timeZoneChange, false),
  new Windows.ApplicationModel.Background.SystemCondition(
  Windows.ApplicationModel.Background.SystemConditionType.userPresent));

Это явно тот случай, когда я хотел бы использовать другую переменную для того, чтобы каждый раз не вводить обозначение пространства имен Windows.ApplicationModel.Background, но вы, по крайней мере, не ошибетесь, читая этот код! В любом случае, тот же пример, в Сценарии 4 и js/global.js, так же показывает использования триггера servicingComplete со вспомогательной функцией registerServicingCompleteTask, которая так же проверят, была ли задача уже зарегистрирована:

"registerServicingCompleteTask": function () {
// Проверяет, была ли уже зарегистрирована фоновая задача servicingComplete.
var iter =
Windows.ApplicationModel.Background.BackgroundTaskRegistration.allTasks.first();
var hascur = iter.hasCurrent;
while (hascur) {
var cur = iter.current.value;
if (cur.name === BackgroundTaskSample.servicingCompleteTaskName) { BackgroundTaskSample.updateBackgroundTaskStatus(
BackgroundTaskSample.servicingCompleteTaskName, true);
return;
}
hascur = iter.moveNext();
}

// Если эта фоновая задача еще не была зарегистрирована.
BackgroundTaskSample.registerBackgroundTask(
BackgroundTaskSample.servicingCompleteTaskEntryPoint,
BackgroundTaskSample.servicingCompleteTaskName,
new Windows.ApplicationModel.Background.SystemTrigger(
Windows.ApplicationModel.Background.SystemTriggerType.servicingComplete, false),
null);
},

В примере, задачи, связанные с данными триггерами, реализованы на C# в WinRT-компонентах, которые можно найти в проекте Tasks решения примера. Я не буду показывать здесь их код, так как мы посмотрим на стандартную структуру компонентов WinRT в Главе 5. Однако, это показывает, что мы можем использовать подход с применением разных языков для фоновых задача. В подобных случаях поле Точка входа (Entry Point) для задач в манифесте указывает на класс/метод C#, который реализует фоновую задачу, как, например, Tasks.ServicingComplete. Если вы перейдете на страницу примера "Фоновая задача" (http://code.msdn.microsoft.com/windowsapps/Background-Task-Sample-9209ade9), вы так же можете загрузить C# и C++-версии примеры для того, чтобы увидеть более структурные варианты.

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >