Какая альтернатива для BackgroundWorker в Windows 8.1 Universal Apps?
Я переношу свое приложение 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
?
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: Отсутствие полного примера кода для начала, мне нет смысла пытаться фактически скомпилировать и протестировать любой код. Таким образом, вышесказанное строго "автор-автор". Этого достаточно для иллюстрации, но я заранее извиняюсь за любые опечатки, которые могут существовать.
Еще в рубрике
- Вопросы
- Win-universal-app
- Какая альтернатива для BackgroundWorker в Windows 8.1 Universal Apps?