Фоновые задачи

Определение задачи

Последнее обновление: 15.04.2017

Приложения на Universal Windows Platform могут использовать фоновые задачи (background tasks). Фоновые задачи позволяют выполнять работу приложения даже в тот момент, когда оно неактивно или не находится на переднем фоне.

И также важно понимать, что фоновые задачи в UWP - это не стандартные службы Windows, которые можно запустить и которые потом смогут непрерывно постоянно выполнять некоторые действия. При создании фоновых задач на UWP мы сталкиваемся с различными ограничениями. Прежде всего фоновым задачам гарантировано выделяется не более 10% ресурсов CPU. Фоновые задачи, за исключением долговременных, должны выполняться по времени в течение 25 секунд и еще дополнительно 5 секунд выделяется на завершение задачи.

В плане оперативной памяти фоновые задачи ограничены объемом примерно в 16 Мб (хотя точное количество доступной оперативной памяти может варьироваться в зависимости от устройства)

Итак, для работы с фоновыми задачами создадим новый проект UWP. Пусть он будет называться BackgroundTaskApp.

Для создания фоновых задач добавим в решение новый проект по типу Windows Runtime Component:

Windows Runtime Component в UWP

Пусть новый проект будет называться MyRuntimeComponent. В итоге в одном решении будут два проекта: основной UWP и проект компонента фоновной задачи.

Из проекта компонента фоновой задачи удалим пустой класс Class1, который добавляется по умолчанию, и добавим новый класс MyBackgroundTask (или переименуем Class1 в MyBackgroundTask). В итоге получится следующая структура проектов:

Background Tasks в UWP

И сразу добавим в главный проект ссылку на проект фоновой задачи.

Компонент фоновой задачи должен реализовать интерфейс IBackgroundTask. Поэтому для начала изменим класс MyBackgroundTask следующим образом:

using Windows.ApplicationModel.Background;

public sealed class MyBackgroundTask : IBackgroundTask
{
    public void Run(IBackgroundTaskInstance taskInstance)
    {
        BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();

        // некоторая работа
		
		_deferral.Complete();
    }
}

Класс фоновой задачи определяет один метод Run(), который срабатывает при запуске фоновой задачи. Собственно он и предназначен для выполнения всех действий, который призвана выполнять данная задача.

Когда необходимо запустить фоновую задачу, система создает объект класса фоновой задачи и объект интерфейса IBackgroundTaskInstance, который служит для передачи параметров в задачу. Объект IBackgroundTaskInstance определяет метод GetDeferral(), который информирует систему о завершении задачи с помощью метода Complete(). IBackgroundTaskInstance также определяет ряд свойств:

  • InstanceId: возвращает id задачи

  • Progress: возвращает или устанавливает статус выполнения фоновой задачи

  • Task: получает доступ к фоновой задаче

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

using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Storage;

public sealed class MyBackgroundTask : IBackgroundTask
{
    volatile bool _cancelRequested = false; // прервана ли задача

    public async void Run(IBackgroundTaskInstance taskInstance)
    {
        // оценка стоимости выполнения задачи для приложения
        var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
        if (cost == BackgroundWorkCostValue.High)
            return;

        // обрабатываем прерывание задачи
        var cancel = new CancellationTokenSource();
        taskInstance.Canceled += (s, e) =>
        {
            cancel.Cancel();
            cancel.Dispose();
            _cancelRequested = true;
        };

        BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();

        await DoWork(taskInstance);

        _deferral.Complete();
    }

    private async Task DoWork(IBackgroundTaskInstance taskInstance)
    {
        // получаем локальные настройки приложения
		var settings = ApplicationData.Current.LocalSettings;
        
		int number = (int)settings.Values["number"];
        uint result = 1;
        for (uint progress = 1; progress <= number; progress++)
        {
            if (_cancelRequested) // если задача прервана, выходим из цикла
            {
                break;
            }

            result *= progress;
            await Task.Delay(1500); // имитация долгого выполнения
			// рассчет процентов выполнения
            taskInstance.Progress = (uint)(progress * 100 / number); // 1 * 100 / 6
        }
            
        settings.Values["factorial"] = result;
    }
}

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

var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
if (cost == BackgroundWorkCostValue.High)
    return;

Далее устанавливаем действия на случай прерывания задачи:

var cancel = new CancellationTokenSource();
taskInstance.Canceled += (s, e) =>
{
    cancel.Cancel();
    cancel.Dispose();
    _cancelRequested = true;
};

Затем идут непосредственно действия по вычислению факториала, которые сосредоточены в методе DoWork. Вначале получаем из локальных настроек число. Если задача прервана, выходим из цикла. Для задержки выполнения, чтобы в пользовательском интерфейсе мы смогли бы увидеть процесс выполнения, используется метод Task.Delay(1500). Однако опять же надо помнить, что мы ограничены 25 секундами, поэтому надо учитывать при временных задержках.

Также надо отметить установку прогресса задачи:

taskInstance.Progress = (uint)(progress * 100 / number);

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

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850