Поддержание производительности при работе приложения в фоновом режиме — фоновые задачи

В своей предыдущей статье (Поддержание производительности при работе приложения в фоновом режиме) я рассказал о модели фонового режима в Windows 8 и о том, как приложение может работать, даже когда оно не отображается на экране из соображений энергосбережения. Сегодня я расскажу о фоновых задачах и о том, как приложение может выполнять код в фоновом режиме, даже когда оно приостановлено. Я опишу два стандартных сценария с примерами кода, в которых показано, как организовать выполнение кода приложения в фоновом режиме, периодическую загрузку электронной почты с 15-минутным интервалом с помощью приложения, отображаемого на экране блокировки, а также работу приложения в фоновом режиме при питании от сети переменного тока.

Введение

Триггеры фоновых задач предназначены для различных сценариев и приложений, поэтому они имеют разные требования и ограничения на управление ресурсами. Некоторые триггеры фоновых задач предназначены для приложений, которые всегда должны оставаться в актуальном состоянии (например, приложения электронной почты или VOIP), тогда как другие поддерживают более гибкие сценарии (например, выполнение задач обслуживания при работе от сети питания или при изменении определенных системных условий). Приложения, которые всегда должны поддерживаться в актуальном состоянии, должны отображаться на экране блокировки. Таких приложений может быть не более 7; я расскажу о них подробнее далее в этой статье. Приложения, выполняющие незапланированную работу, могут использовать триггер обслуживания, который срабатывает при работе от сети питания, или некоторые системные триггеры. Для использования этих триггеров приложению не нужно размещаться на экране блокировки. Приложения, отображающиеся на экране блокировки, имеют менее строгие ограничения на управление ресурсами, так как они должны выполняться чаще (вот почему их число ограничено, и их назначением управляет пользователь). Далее я расскажу об этом более подробно.

Если требуется просто обновлять контент приложения, фоновые задачи использовать необязательно. Для этого можно использовать динамические плитки или запланированные уведомления, как было показано в статье Эффективное использование плиток. А теперь давайте перейдем к коду.

Выполнение работы в фоновом режиме при питании от сети переменного тока

Как было показано в предыдущей статье, иногда приложению нужен собственный код для обеспечения функциональных возможностей в фоновом режиме. Предположим, что вам нужно добавить все фотографии из библиотеки изображений в базу данных приложения или обработать их (например, создать эскизы). Это можно сделать, когда приложение выполняется на переднем плане и взаимодействует с пользователем, или использовать фоновые задачи с триггером обслуживания, которые выполняются в фоновом режиме, только когда устройство подключено к сети питания. Триггер обслуживания доступен для любого приложения и для его использования приложению необязательно размещаться на экране блокировки. Преимущество использования фоновой задачи с триггером обслуживания заключается в том, что она гарантированно не будет мешать действиям пользователя и будет выполняться только при работе от сети питания. Поэтому вам не нужно беспокоиться о повышенном энергопотреблении.

В следующем примере кода показана фоновая задача обслуживания, которая вызывает класс ProcessPictures для обработки файлов. Задача выполняется через каждые 8 часов для поиска новых файлов и их обработки. Эта задача также регистрируется в обработчике отмены, так как фоновые задачи обслуживания отменяются при переходе устройства на питание от батареи. В обработчике отмены обработка файлов отменяется. Если приложение не возвращается из обработчика отмены в течение 5 секунд, Windows завершает его. Для понимания этих примеров кода необходимы знания о классах фоновой задачи и триггера. Подробнее об этих классах можно прочитать в документации по пространству имен Windows.ApplicationModel.Background в Центре разработки.

C#
 public sealed class MaintenanceBackgroundTask: IBackgroundTask
{
    private ProcessPictures processPic; 
 
    public MaintenanceBackgroundTask()
    {
        // Code to process the pictures 
processPic = new ProcessPictures(); 
    }

    //Main Run method which is activated every 8 hours
   async void IBackgroundTask.Run(IBackgroundTaskInstance taskInstance)
    {
        taskInstance.Canceled += taskInstance_Canceled;

// Because these methods are async, you must use a deferral 
        // to wait for all of them to complete
        BackgroundTaskDeferral deferral = taskInstance.GetDeferral(); 
        List<StorageFile> list = new List<StorageFile>();
        int count = await processPic.EnumerateFiles(list);
        bool retval = await processPic.ProcessFiles(list); 
        
        deferral.Complete(); 
    }

    // Cancel handler, called whenever the task is canceled
    void taskInstance_Canceled(IBackgroundTaskInstance sender, 
            BackgroundTaskCancellationReason reason)
    {
        // Device is now on DC power, cancel processing of files 
        processPic.Cancel = true; 
    }
}

 

JavaScript
 // This JS lives within maintenanceBackgroundTask.js
var processPic = new processPictures();
var count = 0;
var retval = false;

function onCanceled(cancelSender, cancelReason) {
    // Device is now on DC power, cancel processing of files
    processPic.cancel = true;
}
backgroundTaskInstance.addEventListener("canceled", onCanceled);

var list = [];
processPic.enumerateFiles(list).then(function (value) {
    count = value;
    processPic.processFiles(list).then(function (value) {
        retval = value;
// Call close() to indicate the task is complete when 
// all async methods have completed
        close();
    });
});

                   

В этом примере кода показано, как зарегистрировать фоновую задачу обслуживания из основного приложения. Эта фоновая задача запускается каждые 8 часов и обрабатывает все изображения в библиотеке изображений. Если необходимость в выполнении этой работы отпадает, регистрацию фоновой задачи можно отменить с помощью метода Unregister из класса IBackgroundTaskRegistration.

C#
 //Registering the maintenance trigger background task       
private bool RegisterMaintenanceBackgroundTask()
        {
            BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
            builder.Name = "Maintenance background task"; 
            builder.TaskEntryPoint = "MaintenanceTask.MaintenaceBackgroundTask";
            // Run every 8 hours if the device is on AC power 
            IBackgroundTrigger trigger = new MaintenanceTrigger(480, false);
            builder.SetTrigger(trigger); 
            IBackgroundTaskRegistration task = builder.Register(); 

            return true;
        }

 

JavaScript
 function registerMaintenanceBackgroundTask() 
{
    var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();
    builder.name = "Maintenance background task";
    builder.taskEntryPoint = "js\\maintenanceBackgroundTask.js";
    //Run every 8 hours if the device is on AC power
    var trigger = new Windows.ApplicationModel.Background.MaintenanceTrigger(480, false);
    builder.setTrigger(trigger);
    var task = builder.register();

    return true;
}

Фоновые задачи должны быть объявлены в манифесте приложения. Откройте манифест приложения в Visual Studio и на вкладке "Объявления" добавьте объявления фоновых задач из раскрывающегося меню. Выберите подходящий тип задачи и укажите точку входа (имя класса фоновой задачи) или начальную страницу, если фоновая задача реализована на JavaScript.

Объявления манифеста фоновой задачи 
Рис. 1. Объявления манифеста фоновой задачи

Чтобы просмотреть содержимое манифеста, щелкните его правой кнопкой мыши и выберите "Просмотреть код". Обратите внимание, что задача обслуживания может выполняться только в системном хосте (backgroundTaskHost.exe или wwahost.exe для JavaScript), поэтому атрибут Executable не указывается. Типом задачи триггера обслуживания является systemEvent, как видно в следующем фрагменте манифеста.

 <Extension Category="windows.backgroundTasks" EntryPoint="MaintenanceTask.MaintenaceBackgroundTask">
         <BackgroundTasks>
           <Task Type="systemEvent" />
         </BackgroundTasks>
       </Extension>

В JavaScript вместо атрибута EntryPoint указывается атрибут StartPage.

 <Extension Category="windows.backgroundTasks" StartPage="js\maintenaceBackgroundTask.js">

Подробнее об использовании фоновых задач можно прочитать в техническом документе Введение в фоновые задачи. Информацию об использовании API см. в Центре разработки.

Периодическая загрузка электронной почты с 15-минутным интервалом

В этом примере нам понадобится приложение, которое должно предсказуемо и часто выполняться в фоновом режиме. Этого можно добиться, разместив приложение на экране блокировки.

Пользователь определяет, какие приложения могут поддерживать актуальное состояние с помощью фоновых задач, даже когда устройство Windows 8 не используется, предоставляя им разрешение на отображение на экране блокировки. Это естественный подход, поскольку экран блокировки предназначен для информирования пользователя о приложениях без разблокировки устройства Windows 8. Такая связь действует в обоих направлениях: приложение может использовать эти типы фоновых задач, только если оно размещено на экране блокировки, и наоборот, приложение может отображаться на экране блокировки, только если использует эти типы фоновых задач.

background2_img2

Рис. 2. Пользовательский интерфейс персонализации экрана блокировки и приложения, отображаемые на экране блокировки

Поскольку на экране блокировки можно разместить относительно небольшое количество приложений, ваше приложение должно обладать хорошими функциональными возможностями. В противном случае пользователи скорее всего удалят его, чтобы освободить место для чего-то другого. К приложениям, подходящим для отображения на экране блокировки, относятся приложения связи, например приложение электронной почты, показывающее количество непрочитанных сообщений, приложение календаря, показывающее предстоящие встречи в строке состояния, или приложение для обмена сообщениями, показывающее количество пропущенных сообщений. Дополнительную информацию об экране блокировки, а также о том, какие приложения подходят для размещения на этом экране, можно найти в Центре разработки.

Использование триггера времени для периодической загрузки электронной почты с 15-минутным интервалом

В этом примере кода показано использование класса BackgroundTaskBuilder для регистрации фоновой задачи с триггером времени, которая выполняется каждые 15 минут при наличии подключения к Интернету. Если Интернет недоступен, фоновая задача не выполняется. Вместо этого она ожидает появления подключения к Интернету, а затем запускается автоматически. Это еще одно полезное свойство фоновых задач, предотвращающее выполнение ненужной работы и позволяющее продлить время работы устройства от батареи. Без этого условия код приложения запустился бы, обнаружил отсутствие подключения к сети и возвратил бы ошибку из-за невозможности загрузки почты. Почта будет загружаться независимо от того, находится ли приложение на переднем плане или работает в фоновом режиме. Загрузка также будет выполняться, если устройство находится в режиме ожидания с подключением.

C#
 private bool RegisterTimeTriggerBackgroundTask()
        {
            BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
            builder.Name = "Pop mail background task";
            builder.TaskEntryPoint = "MailClient.PopMailBackgroundTask";
            // Run every 15 minutes if the device has internet connectivity
            IBackgroundTrigger trigger = new TimeTrigger(15, false);
            builder.SetTrigger(trigger);
            IBackgroundCondition condition = 
                new SystemCondition(SystemConditionType.InternetAvailable);
            builder.AddCondition(condition); 
            IBackgroundTaskRegistration task = builder.Register();

            return true;
        }
JavaScript
 function registerTimeTriggerBackgroundTask() 
   {
       var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();
       builder.name = "Pop mail background task";
       builder.taskEntryPoint = "js\\popMailBackgroundTask.js";
       //Run every 15 minutes if the device has internet connectivity
       var trigger = new Windows.ApplicationModel.Background.TimeTrigger(15, false);
       builder.setTrigger(trigger);
       var condition = new Windows.ApplicationModel.Background.SystemCondition(                             
Windows.ApplicationModel.Background.SystemConditionType.internetAvailable);
       builder.addCondition(condition);
       var task = builder.register();

       return true;
   }

Триггер времени доступен только для приложений, отображаемых на экране блокировки, как описано выше. Для программного запроса размещения на экране блокировки необходимо использовать класс BackgroundExecutionManager. Если пользователь не размещает приложение на экране блокировки, соответствующие фоновые задачи не выполняются. В таком случае можно использовать фоновую задачу, для которой не требуется экран блокировки, или выполнять работу, когда пользователь использует ваше приложение. Пользователь не будет получать повторные запросы, система предлагает добавить приложение на экран блокировки только один раз. Если пользователь отказался от добавления приложения на экран блокировки, он сможет сделать это позднее вручную.

C#
 public async Task<bool> ObtainLockScreenAccess()
        {
            BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
            
            if (status == BackgroundAccessStatus.Denied || status == BackgroundAccessStatus.Unspecified)
            {
                return false; 
            }

            return true; 
        }

 

JavaScript
 function obtainLockScreenAccess()
   {
       Windows.ApplicationModel.Background.BackgroundExecutionManager.requestAccessAsync().then(function (status) {
           if (status === Windows.ApplicationModel.Background.BackgroundAccessStatus.denied || 
               status === Windows.ApplicationModel.Background.BackgroundAccessStatus.unspecified){
               return false;
           }
           return true;
       });
   }

В этом примере кода показана основная фоновая задача, которая загружает почту каждые 15 минут. Здесь не приводится подробное описание обновления плитки и индикаторов событий приложения, поскольку эта тема уже рассматривалась в статье Эффективное использование плиток.

C#
 void IBackgroundTask.Run(IBackgroundTaskInstance taskInstance)
        {
            int count = popmailClient.GetNewMails(); 
            // Update badge on lock screen with the mail count 
            popmailClient.UpdateLockScreenBadgeWithNewMailCount(count);

            IList<string> mailheaders = popmailClient.GetNewMailHeaders(); 
            // Update app tile with the subjects of the email 
            popmailClient.UpdateTileWithSubjects(mailheaders); 
        }

 

JavaScript
 //This JS lives within popMailBackgroundTask.js
    var count = popmailClient.getNewMails();
    // Update badge on lock screen with the mail count
    popmailClient.updateLockScreenBadgeWithNewmailCount(count);

    var mailheaders = popmailClient.getnewMailHeaders();
    // Update app tile with the subjects of the email
    popmailClient.updatetileWithSubjects(mailheaders);
    close();

Для добавления приложения на экран блокировки необходимо указать тип уведомлений экрана блокировки, доступный на вкладке "Интерфейс приложения" манифеста.

Рис. 3. Объявления манифеста фоновой задачи

Это фрагмент кода манифеста (созданного мастером) приложения, которое должно размещаться на экране блокировки и отображать на нем индикаторы событий и всплывающие уведомления. Задача с триггером времени может выполняться только в системном хосте (backgroundTaskHost.exe или wwahost.exe для JavaScript), поэтому атрибут Executable не указывается. Типом задачи является timer, как видно из манифеста.

 <LockScreen Notification="badgeAndTileText" BadgeLogo="Images\badgelogo.png" />
      <DefaultTile ShowName="allLogos" WideLogo="Images\tile-sdk.png" ShortName="LockScreen CS" />
      <SplashScreen Image="Images\splash-sdk.png" BackgroundColor="#FFFFFF" />
    </VisualElements>
    <Extensions>
      <Extension Category="windows.backgroundTasks" EntryPoint="MailClient.PopMailBackgroundTask">
        <BackgroundTasks>
          <Task Type="timer" />
        </BackgroundTasks>
      </Extension>

В JavaScript вместо атрибута EntryPoint указывается атрибут StartPage.

 <Extension Category="windows.backgroundTasks" StartPage="js\popMailBackgroundTask.js"

 

Дополнительные сценарии

Вы можете создавать более сложные приложения для VOIP, обмена мгновенными сообщениями или отправки электронной почты, используя другие триггеры фоновых задач, например триггер канала управления или триггер push-уведомлений. Их описание выходит за рамки этой статьи. Более подробная информация об этих триггерах содержится в техническом документе Работа с сетью в фоновом режиме.

Управление ресурсами для фоновых задач

Как уже отмечалось, фоновые задачи разрабатывались с учетом необходимости оптимизации энергопотребления, поэтому к ним применяются ограничения на использование ресурсов ЦП и сети. Это не позволяет фоновым приложениям истощать заряд батареи устройства без ведома пользователя. Если приложение активно и пользователь взаимодействует с ним на переднем плане, к его фоновым задачам не применяются ограничения на использование ресурсов ЦП и сети.

Ограничения ресурсов ЦП

Каждое приложение на экране блокировки получает 2 секунды времени ЦП каждые 15 минут. Это время может использоваться всеми фоновыми задачами приложения. По окончании 15-минутного интервала каждое приложение на экране блокировки получает еще 2 секунды времени ЦП, которое может использоваться его фоновыми задачами. Не использованное в течение 15-минутного интервала время ЦП теряется: приложение не может его накапливать. Каждое приложение, которое не находится на экране блокировки, получает 1 секунду времени ЦП каждые 2 часа. Если приложение использует все выделенное ему время ЦП, его фоновые задачи приостанавливаются до следующего возобновления квоты ЦП.

Под временем использования ЦП подразумевается фактическое используемое приложением время ЦП, а не время выполнения фоновой задачи. Например, если код фоновой задачи ожидает ответа от удаленного сервера и фактически не использует ЦП, время ожидания не учитывается.

Ограничения ресурсов ЦП для фоновых задач

 

Квота ресурсов ЦП

Период обновления

Приложение, отображаемое на экране блокировки

2 секунды времени ЦП

15 минут

Приложение, которое не отображается на экране блокировки

1 секунда времени ЦП

2 часа

Ограничения сетевых ресурсов

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

Следующая таблица характеризует использование сети WiFi с ограничением ресурсов при минимальном уровне помех.

 

Глобальный пул

Иногда этих фиксированных ограничений ресурсов недостаточно, даже при назначении квоты для каждого приложения. Для таких ситуаций существует общий глобальный пул, из которого приложения могут запрашивать ресурсы ЦП и сети.

Сведения о фоновых задачах, глобальном пуле и его ограничениях на управление ресурсами, а также рекомендации можно найти в техническом документе Введение в фоновые задачи. В нем также представлен образец проекта с исходным кодом.

Подводя итоги

Итак, на вопрос "может ли приложение выполнять работу, когда оно не отображается на экране? " можно уверенно ответить "да". Модель фонового режима в Windows 8 позволяет приложениям осуществлять основные сценарии, необходимые конечным пользователям, такие как загрузка файлов, воспроизведение аудио, обновление электронной почты в фоновом режиме или выполнение задач обслуживания при работе от сети питания. Поскольку платформа тщательно отслеживает эти действия, фоновая работа будет оказывать минимальное влияние на скорость отклика приложения переднего плана и время работы устройства от батареи.

Дополнительные технические сведения о модели фонового режима в Windows 8 можно найти в соответствующих технических документах в Центре разработки. Если у вас есть вопросы, задайте их в комментариях и мы постараемся ответить на них как можно подробнее.

— Хари Пулапака (Hari Pulapaka), руководитель программы, Windows.

Благодарю Джейка Сабульски (Jake Sabulsky), Джонни Брегара (Johnny Bregar), Кайла Бека (Kyle Beck) и Сухаила Халида (Suhail Khalid) за их вклад в создание этой статьи, а также многих других, включая Александра Коррадини (Alexander Corradini), Аруна Кишана (Arun Kishan), Бена Сроура (Ben Srour), Иена Легроу (Ian LeGrow), Джейми Шварца (Jamie Schwartz) и Джона Шихана (John Sheehan) , за их ценные отзывы.

Ресурсы

Ссылка

Тип

Основные идеи

Введение в фоновые задачи

Технический документ

Основные сведения о фоновых задачах

Пространство имен API модели фонового режима

Документация

Пространство имен API модели фонового режима

Образец проекта фоновых задач

Образец проекта

Демонстрация использования фоновых задач

Обзор экрана блокировки

Ключевая документация

Описание экрана блокировки и рекомендации по работе с ним

Работа с сетью в фоновом режиме

Технический документ

Инструкции по разработке сложных приложений (таких как приложения VOIP и обмена мгновенными сообщениями) с использованием фоновых задач.

Эффективное использование плиток

Статья

Инструкции по эффективному использованию плиток