背水一战 Windows 10 (118) - 后台任务: 后台下载任务(任务分组,并行或串行执行,组完成后通知)
背水一战 Windows 10 (118) - 后台任务: 后台下载任务(任务分组,并行或串行执行,组完成后通知)
作者:webabcd
介绍
背水一战 Windows 10 之 后台任务
- 后台下载任务(任务分组,并行或串行执行,组完成后通知)
示例
演示后台下载任务的分组,以及如何设置组内任务是并行执行还是串行执行,以及组任务全部完成后如何 toast 或 tile 通知)
BackgroundTask/TransferModel.cs
/* * 扩展了 DownloadOperation 和 UploadOperation,用于 MVVM 绑定数据 */ using System; using System.ComponentModel; using Windows.Networking.BackgroundTransfer; namespace Windows10.BackgroundTask { public class TransferModel : INotifyPropertyChanged { public DownloadOperation DownloadOperation { get; set; } public UploadOperation UploadOperation { get; set; } public string Source { get; set; } public string Destination { get; set; } private string _progress; public string Progress { get { return _progress; } set { _progress = value; RaisePropertyChanged("Progress"); } } public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } }
BackgroundTask/TransferGroup.xaml
<Page x:Class="Windows10.BackgroundTask.TransferGroup" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows10.BackgroundTask" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="10 0 10 10"> <ScrollViewer Name="scrollViewer" Height="100" Margin="5"> <TextBlock Name="lblMsg" TextWrapping="Wrap" /> </ScrollViewer> <Button Name="btnAddDownload" Content="新增一组(3 个)下载任务,可以指定其是并发还是串行,当这一组的所有任务都完成后弹出通知" Margin="5" Click="btnAddDownload_Click" /> <Button Name="btnCancel" Content="取消所有下载任务" Margin="5" Click="btnCancel_Click" /> <ListView Name="listView" Height="286" Padding="5"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Margin="0 5" Background="Blue"> <TextBlock Text="{Binding Source}" Margin="5" /> <TextBlock Text="{Binding Destination}" Margin="5" /> <TextBlock Text="{Binding Progress}" Margin="5" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackPanel> </Grid> </Page>
BackgroundTask/TransferGroup.xaml.cs
/* * 演示后台下载任务的分组,以及如何设置组内任务是并行执行还是串行执行,以及组任务全部完成后如何 toast 或 tile 通知) * * BackgroundTransferGroup - 后台下载任务的分组对象 * static BackgroundTransferGroup CreateGroup(string name) - 创建指定分组标识的 BackgroundTransferGroup 对象 * Name - 分组标识(只读) * TransferBehavior - 组内下载任务的执行方式,BackgroundTransferBehavior 枚举 * Parallel - 并行 * Serialized - 串行 * * BackgroundDownloader - 后台下载任务管理器 * TransferGroup - 设置或获取分组对象(BackgroundTransferGroup 类型) * static GetCurrentDownloadsForTransferGroupAsync(BackgroundTransferGroup group) - 获取指定组的所有下载任务 * * DownloadOperation - 下载任务对象 * TransferGroup - 获取此下载任务的分组对象(BackgroundTransferGroup 类型) */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; using System.Linq; using Windows.Networking.BackgroundTransfer; using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.Web; using Windows.UI.Notifications; using Windows.Data.Xml.Dom; namespace Windows10.BackgroundTask { public sealed partial class TransferGroup : Page { // 用于后台任务的分组(通过组名标识后台任务) private BackgroundTransferGroup _group = BackgroundTransferGroup.CreateGroup("my_group"); // 下载任务的集合 private ObservableCollection<TransferModel> _transfers = new ObservableCollection<TransferModel>(); // 所有下载任务的关联的 CancellationTokenSource 对象 private CancellationTokenSource _cancelToken = new CancellationTokenSource(); public TransferGroup() { this.InitializeComponent(); Init(); } private async void Init() { // 指定组内任务并行执行 _group.TransferBehavior = BackgroundTransferBehavior.Parallel; listView.ItemsSource = _transfers; // 加载指定组的下载任务 await LoadDownloadAsync(); } // 加载指定组的下载任务 private async Task LoadDownloadAsync() { IReadOnlyList<DownloadOperation> downloads = null; try { // 获取指定组的下载任务 downloads = await BackgroundDownloader.GetCurrentDownloadsForTransferGroupAsync(_group); } catch (Exception ex) { WriteLine(ex.ToString()); return; } if (downloads.Count > 0) { List<Task> tasks = new List<Task>(); foreach (DownloadOperation download in downloads) { // 监视指定的后台下载任务 tasks.Add(HandleDownloadAsync(download, false)); } await Task.WhenAll(tasks); } } // 新增一组(3 个)下载任务 private async void btnAddDownload_Click(object sender, RoutedEventArgs e) { BackgroundDownloader backgroundDownloader = new BackgroundDownloader(); // 指定分组 backgroundDownloader.TransferGroup = _group; // 组任务全部成功后弹出指定的 toast 通知(类似的还有 SuccessTileNotification, FailureToastNotification, FailureTileNotification) backgroundDownloader.SuccessToastNotification = GetToastNotification(_group.Name); List<DownloadOperation> downloads = new List<DownloadOperation>(); for (int i = 0; i < 3; i++) { Uri sourceUri = new Uri("https://files.cnblogs.com/webabcd/Windows10.rar", UriKind.Absolute); StorageFile destinationFile; try { // 保存的目标地址(别忘了在 Package.appxmanifest 中配置好 <Capability Name="documentsLibrary" /> 和 .rar 类型文件的关联) StorageFolder storageFolder = await KnownFolders.GetFolderForUserAsync(null, KnownFolderId.DocumentsLibrary); destinationFile = await storageFolder.CreateFileAsync("Windows10.rar", CreationCollisionOption.GenerateUniqueName); } catch (Exception ex) { WriteLine(ex.ToString()); return; } // 创建一个后台下载任务 DownloadOperation download = backgroundDownloader.CreateDownload(sourceUri, destinationFile); downloads.Add(download); } // 处理并监视组内的后台下载任务 Task[] tasks = new Task[downloads.Count]; for (int i = 0; i < downloads.Count; i++) { tasks[i] = HandleDownloadAsync(downloads[i], true); } await Task.WhenAll(tasks); } /// <summary> /// 处理并监视组内的后台下载任务 /// </summary> /// <param name="download">后台下载任务</param> /// <param name="isNew">是否是新增的任务</param> private async Task HandleDownloadAsync(DownloadOperation download, bool isNew) { try { // 构造显示用的相关数据 TransferModel transfer = new TransferModel(); transfer.DownloadOperation = download; transfer.Source = download.RequestedUri.ToString(); transfer.Destination = download.ResultFile.Path; transfer.Progress = download.Progress.Status.ToString() + ": 0 / 0"; _transfers.Add(transfer); WriteLine("Task Count: " + _transfers.Count.ToString()); // 当下载进度发生变化时的回调函数 Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress); if (isNew) await download.StartAsync().AsTask(_cancelToken.Token, progressCallback); // 启动一个后台下载任务 else await download.AttachAsync().AsTask(_cancelToken.Token, progressCallback); // 监视已存在的后台下载任务 // 下载完成后获取服务端的响应信息 ResponseInformation response = download.GetResponseInformation(); WriteLine("Completed: " + response.ActualUri + ", HttpStatusCode: " + response.StatusCode.ToString()); } catch (TaskCanceledException) // 调用 CancellationTokenSource.Cancel() 后会抛出此异常 { WriteLine("Canceled: " + download.Guid); } catch (Exception ex) { // 将异常转换为 WebErrorStatus 枚举,如果获取到的是 WebErrorStatus.Unknown 则说明此次异常不是涉及 web 的异常 WebErrorStatus error = BackgroundTransferError.GetStatus(ex.HResult); WriteLine(ex.ToString()); } finally { _transfers.Remove(_transfers.First(p => p.DownloadOperation == download)); } } // 进度发生变化时,更新 TransferModel 的 Progress private void DownloadProgress(DownloadOperation download) { TransferModel transfer = _transfers.First(p => p.DownloadOperation == download); transfer.Progress = download.Progress.Status.ToString() + ": " + download.Progress.BytesReceived.ToString("#,0") + " / " + download.Progress.TotalBytesToReceive.ToString("#,0"); } // 取消全部后台下载任务 private void btnCancel_Click(object sender, RoutedEventArgs e) { _cancelToken.Cancel(); _cancelToken.Dispose(); _cancelToken = new CancellationTokenSource(); } // 向 lblMsg 中追加一行文本 private void WriteLine(string message) { lblMsg.Text += message; lblMsg.Text += Environment.NewLine; scrollViewer.ChangeView(0, scrollViewer.ScrollableHeight, 1f); } private ToastNotification GetToastNotification(string groupName) { string toastXml = $@" <toast activationType='foreground'> <visual> <binding template='ToastGeneric'> <text>toast - title</text> <text>组 {groupName} 中的下载任务全部完成了</text> </binding> </visual> </toast>"; XmlDocument toastDoc = new XmlDocument(); toastDoc.LoadXml(toastXml); return new ToastNotification(toastDoc); } } }
OK
[源码下载]