Какая альтернатива для BackgroundWorker в Windows 8.1 Universal Apps?

55
5

Я переношу свое приложение Windows Phone в Windows Universal Apps. В приложении "Телефон" я использовал BackgroundWorker для извлечения базы данных, а затем в пользовательском интерфейсе. Ниже приведен класс, который я подготовил в Windows Phone 8 и как он был вызван.

public class TestBackgroundWorker
{
private BackgroundWorker backgroundWorker;
ProgressIndicator progressIndicator;

public delegate void functionToRunInBackground();
public functionToRunInBackground currentFunctionToExecute;

public delegate void callbackFunction();
public callbackFunction functionToSendResult;

private bool isCancellationSupported;

private string message;

/// <summary>
///
/// </summary>
/// <param name="functionNameToExecute">specifies function name to be executed in background</param>
/// <param name="isCancellable">Flag which specifies whether the operation is cancellable or not</param>
/// <param name="functionNameWhichGetsResult">Specifies call back function to be executed after the completion of operation</param>
public MCSBackgroundWorker(functionToRunInBackground functionNameToExecute, bool isCancellable, string messageToDisplay, callbackFunction functionNameWhichGetsResult)
{
currentFunctionToExecute = functionNameToExecute;
functionToSendResult = functionNameWhichGetsResult;
isCancellationSupported = isCancellable;
message = messageToDisplay;
backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerSupportsCancellation = isCancellable;
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
deactivateProgressIndicator();
functionToSendResult();
}

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (currentFunctionToExecute != null)
{
currentFunctionToExecute();
}
}

public void cancelBackgroundOperation()
{
if (isCancellationSupported == true)
{
backgroundWorker.CancelAsync();
}
}

public void Start()
{
backgroundWorker.RunWorkerAsync();
activateProgressIndicator();
}

void activateProgressIndicator()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
SystemTray.SetIsVisible(currentPage, true);
SystemTray.SetOpacity(currentPage, 0.5);
SystemTray.SetBackgroundColor(currentPage, Colors.White);
SystemTray.SetForegroundColor(currentPage, Colors.Black);

progressIndicator = new ProgressIndicator();
progressIndicator.IsVisible = true;
progressIndicator.IsIndeterminate = true;
progressIndicator.Text = message;

SystemTray.SetProgressIndicator(currentPage, progressIndicator);

});

}

void deactivateProgressIndicator()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (progressIndicator != null)
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
progressIndicator.IsVisible = false;
SystemTray.SetIsVisible(currentPage, false);

}

});
}

public bool isBackgroundWorkerBusy()
{
return backgroundWorker != null ? backgroundWorker.IsBusy : false;
}

}

}

И назовите это, как показано ниже, чтобы запустить процесс в фоновом режиме.

private void loadReports()
{
bgWorker = new TestBackgroundWorker(loadReportsFromDB, true, "Loading...", showReports);
bgWorker.Start();
}

Здесь loadReprtsFromDB и showReports - это две функции.

Вопросов:

Может ли кто-нибудь предложить, как добиться того же в Windows 8.1?

Есть ли альтернатива для PhoneApplicationService.Current.State?

спросил(а) 2015-03-11T08:57:00+03:00 4 года, 9 месяцев назад
1
Решение
54

IMHO, даже для рабочего стола, классы Task<T> и Progress<T> предлагают отличную альтернативу BackgroundWorker, и оба они поддерживаются на Windows Phone 8.1. Класс Task<T> предоставляет механизм для запуска, а затем чисто ждать фоновых операций, в то время как класс Progress<T> предоставляет механизм отчетности о прогрессе (не часть вашего примера или вопроса, но я упоминаю об этом, потому что одна вещь Task вместе с async/await не предоставляется из BackgroundWorker).

Ваш пример может быть изменен на следующее:

public class TestBackgroundWorker
{
private Task _task;
private CancellationTokenSource _cancelSource;

public CancellationToken CancellationToken
{
get { return _cancelSource != null ? _cancelSource.Token : null; }
}

ProgressIndicator progressIndicator;

public readonly Action<TestBackgroundWorker> currentFunctionToExecute;

private string message;

/// <summary>
///
/// </summary>
/// <param name="functionNameToExecute">specifies function name to be executed in background</param>
/// <param name="isCancellable">Flag which specifies whether the operation is cancellable or not</param>
/// <param name="functionNameWhichGetsResult">Specifies call back function to be executed after the completion of operation</param>
public MCSBackgroundWorker(Action<TestBackgroundWorker> functionNameToExecute, bool isCancellable, string messageToDisplay)
{
currentFunctionToExecute = functionNameToExecute;
_cancelSource = isCancellable ? new CancellationTokenSource() : null;
message = messageToDisplay;
}

public void cancelBackgroundOperation()
{
if (_cancelSource != null)
{
_cancelSource.Cancel();
}
}

public async Task Start()
{
activateProgressIndicator();
_task = Task.Run(() => currentFunctionToExecute(this));
await _task;
_task = null;
deactivateProgressIndicator();
}

void activateProgressIndicator()
{
// In theory, you should not need to use Dispatcher here with async/await.
// But without a complete code example, it impossible for me to
// say for sure, so I've left it as-is.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
SystemTray.SetIsVisible(currentPage, true);
SystemTray.SetOpacity(currentPage, 0.5);
SystemTray.SetBackgroundColor(currentPage, Colors.White);
SystemTray.SetForegroundColor(currentPage, Colors.Black);

progressIndicator = new ProgressIndicator();
progressIndicator.IsVisible = true;
progressIndicator.IsIndeterminate = true;
progressIndicator.Text = message;

SystemTray.SetProgressIndicator(currentPage, progressIndicator);
});
}

void deactivateProgressIndicator()
{
// Likewise.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (progressIndicator != null)
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
progressIndicator.IsVisible = false;
SystemTray.SetIsVisible(currentPage, false);
}
});
}

public bool isBackgroundWorkerBusy()
{
return _task != null;
}
}

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

private async Task loadReports()
{
bgWorker = new TestBackgroundWorker(loadReportsFromDB, true, "Loading...");
await bgWorker.Start();
showReports();
}

void loadReportsFromDB(TaskBackgroundWorker worker)
{
while (...)
{
if (worker.CancellationToken.IsCancellationRequested)
{
return; // or whatever
}
}
}

Для того, чтобы иметь дело с отменой, то functionNameToExecute делегат должен был бы быть для метода, который принимает экземпляр TaskBackgroundWorker в качестве параметра, так что он может получить CancellationToken значение свойства для проверки отмены ( по аналогии с DoWork() обработчик событий... хотя ваш пример кода фактически не предполагал никакого механизма, при котором фактический код операции фона даже обнаруживал бы отмену).

Обратите внимание, что с помощью async/await ваша задача также может вернуть значение, если хотите, с помощью типа Task<T> вместо Task. Вышеприведенный пример может быть легко изменен для его соответствия, и эта функция async/await является одной из самых больших причин, которые я предпочитаю использовать в BackgroundWorker (у которого нет чистого механизма, поддерживающего компилятор для возврата результатов из фоновой операции).

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

ответил(а) 2015-03-11T09:27:00+03:00 4 года, 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема