Windows 8.1 通用应用程序中 BackgroundWorker 的替代方案是什么?

What is the alternative for BackgroundWorker in Windows 8.1 Universal Apps?

我正在将我的 Windows Phone 应用程序迁移到 Windows 通用应用程序。在Phone App中,我使用BackgroundWorker进行数据库检索,然后在UI中展示。下面是我在WindowsPhone8中准备的class以及它的调用方式。

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是两个函数

问题:

  1. 任何人都可以建议如何在 Windows 8.1 中实现同样的事情吗?

  2. PhoneApplicationService.Current.State还有其他选择吗?

恕我直言,即使对于桌面,Task<T>Progress<T> classes 提供了 BackgroundWorker 的一个很好的替代方案,并且它们都在 [=47] 上受支持=] Phone 8.1。 Task<T> class 提供启动然后干净地等待后台操作的机制,而 Progress<T> class 提供报告进度的机制(不是您的示例或问题的一部分, 但我提到它是因为 Taskasync/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's 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,如果您愿意,您的任务也可以 return 一个值,通过 Task<T> 类型而不是 Task .上面的示例可以很容易地修改以适应这一点,async/await 的特性是我比 BackgroundWorker 更喜欢它的最大原因之一(它没有干净的、编译器支持的return后台操作结果的机制。

警告: 缺少完整的代码示例作为开始,我没有必要尝试实际编译和测试任何代码。所以上面严格来说是"browser-authored"。出于说明的目的,它应该足够了,但对于可能存在的任何拼写错误,我提前表示歉意。