[源码下载]

背水一战 Windows 10 (119) - 后台任务: 后台下载任务(任务分组,组完成后触发后台任务)

作者:webabcd

介绍
背水一战 Windows 10 之 后台任务

  • 后台下载任务(任务分组,组完成后触发后台任务)

示例
演示后台下载任务的分组,以及组任务全部完成后如何触发后台任务
/BackgroundTaskLib/BackgroundTaskTransfer.cs

/*
* 后台任务,用于演示指定的一组后台下载任务全部完成后如何触发此后台任务
*
* BackgroundTransferCompletionGroup - 分组对象(用于实现“组任务全部完成后触发后台任务”)
* Enable() - 启用“组任务全部完成后触发后台任务”的功能
* IsEnabled - 是否启用了“组任务全部完成后触发后台任务”的功能(只读)
* Trigger - “组任务全部完成后触发后台任务”的触发器
*
* BackgroundDownloader - 后台下载任务管理器
* BackgroundDownloader(BackgroundTransferCompletionGroup completionGroup) - 通过指定的 BackgroundTransferCompletionGroup 对象实例化 BackgroundDownloader 对象
* CompletionGroup - 获取关联的 BackgroundTransferCompletionGroup 对象
*
*
* 注:需要在 Package.appxmanifest 添加“后台任务”声明,支持的任务类型选择“系统事件”,并指定 EntryPoint(后台任务的类全名),类似如下:
* <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTaskLib.BackgroundTaskTransfer">
* <BackgroundTasks>
* <Task Type="systemEvent" />
* </BackgroundTasks>
* </Extension>
*/ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Data.Xml.Dom;
using Windows.Networking.BackgroundTransfer;
using Windows.UI.Notifications; namespace BackgroundTaskLib
{
public sealed class BackgroundTaskTransfer : IBackgroundTask
{
// 所注册的后台任务的名称
public static string TaskName { get; set; } = "Transfer";
// 所注册的后台任务的 EntryPoint,即后台任务的类全名
public static string TaskEntryPoint { get; set; } = "BackgroundTaskLib.BackgroundTaskTransfer"; // 实现 IBackgroundTask 接口,其只有一个方法,即 Run()
public void Run(IBackgroundTaskInstance taskInstance)
{
// 获取 BackgroundTransferCompletionGroupTriggerDetails 对象,如果不是 null 则说明是 BackgroundTransferCompletionGroup.Trigger 触发的
BackgroundTransferCompletionGroupTriggerDetails details = taskInstance.TriggerDetails as BackgroundTransferCompletionGroupTriggerDetails;
if (details == null)
{
return;
} // 获取下载任务列表
List<DownloadOperation> failedDownloads = new List<DownloadOperation>();
int successTotal = ;
foreach (DownloadOperation download in details.Downloads)
{
if (IsFailed(download))
{
// 保存失败的下载任务列表,稍后会重试
failedDownloads.Add(download);
}
else
{
successTotal++;
}
} if (failedDownloads.Count > )
{
// 重新下载失败的任务
RetryDownloads(failedDownloads);
} // 此后台任务执行完毕,弹出指定的 toast 通知
ShowToast(successTotal, failedDownloads.Count);
} // 判断指定的下载任务是否失败了
private bool IsFailed(DownloadOperation download)
{
BackgroundTransferStatus status = download.Progress.Status;
if (status == BackgroundTransferStatus.Error || status == BackgroundTransferStatus.Canceled)
{
return true;
} ResponseInformation response = download.GetResponseInformation();
if (response.StatusCode != )
{
return true;
} return false;
} // 重新下载指定的任务
private void RetryDownloads(IEnumerable<DownloadOperation> downloads)
{
// 注册指定的后台任务,并返回与此后台任务相关联的 BackgroundDownloader 对象
BackgroundDownloader downloader = BackgroundTaskTransfer.RegisterBackgroundTaskAndReturnBackgrounDownloader(); foreach (DownloadOperation download in downloads)
{
// 创建并启动后台任务
DownloadOperation downloadNew = downloader.CreateDownload(download.RequestedUri, download.ResultFile);
Task<DownloadOperation> startTask = downloadNew.StartAsync().AsTask();
} // 启用“组任务全部完成后触发后台任务”的功能
downloader.CompletionGroup.Enable();
} // 注册指定的后台任务,并返回与此后台任务相关联的 BackgroundDownloader 对象
// 后台任务的注册一般是在前台代码中写的,但是由于本例还有一个后台任务执行完成后重新下载失败任务的功能,所以这部分代码写在后台代码中就很合理了
public static BackgroundDownloader RegisterBackgroundTaskAndReturnBackgrounDownloader()
{
// 根据指定 BackgroundTransferCompletionGroup 对象创建一个 BackgroundDownloader 对象
BackgroundTransferCompletionGroup completionGroup = new BackgroundTransferCompletionGroup();
BackgroundDownloader downloader = new BackgroundDownloader(completionGroup); // 注册一个后台任务,并指定触发器为 BackgroundTransferCompletionGroup.Trigger
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.Name = BackgroundTaskTransfer.TaskName;
builder.TaskEntryPoint = BackgroundTaskTransfer.TaskEntryPoint;
builder.SetTrigger(completionGroup.Trigger);
BackgroundTaskRegistration task = builder.Register(); return downloader;
} // 后台任务执行完成后弹出 toast 通知
private void ShowToast(int successTotal, int failureTotal)
{
string toastXml = $@"
<toast activationType='foreground'>
<visual>
<binding template='ToastGeneric'>
<text>toast - title</text>
<text>下载任务成功数: {successTotal}</text>
<text>下载任务失败数: {failureTotal}</text>
</binding>
</visual>
</toast>"; XmlDocument toastDoc = new XmlDocument();
toastDoc.LoadXml(toastXml); ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastDoc));
}
}
}

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/TransferBackground.xaml

<Page
x:Class="Windows10.BackgroundTask.TransferBackground"
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="btnAddDownloadAndRegister" Content="新增一组(3 个)下载任务,并注册指定的后台任务,当这一组下载任务全部完成后触发一个后台任务。" Margin="5" Click="btnAddDownloadAndRegister_Click" />
<Button Name="btnRemoveDownloadAndUnregister" Content="取消下载任务并注销指定的后台任务" Margin="5" Click="btnRemoveDownloadAndUnregister_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/TransferBackground.xaml.cs

/*
* 演示后台下载任务的分组,以及组任务全部完成后如何触发后台任务
*
* BackgroundTransferCompletionGroup - 分组对象(用于实现“组任务全部完成后触发后台任务”)
* Enable() - 启用“组任务全部完成后触发后台任务”的功能
* IsEnabled - 是否启用了“组任务全部完成后触发后台任务”的功能(只读)
* Trigger - “组任务全部完成后触发后台任务”的触发器
*
* BackgroundDownloader - 后台下载任务管理器
* BackgroundDownloader(BackgroundTransferCompletionGroup completionGroup) - 通过指定的 BackgroundTransferCompletionGroup 对象实例化 BackgroundDownloader 对象
* CompletionGroup - 获取关联的 BackgroundTransferCompletionGroup 对象
*
*
* 注:需要引用后台任务项目,相关代码参见 BackgroundTaskLib/BackgroundTaskTransfer.cs
*/ using BackgroundTaskLib;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Networking.BackgroundTransfer;
using Windows.Storage;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Windows.Web; namespace Windows10.BackgroundTask
{
public sealed partial class TransferBackground : Page
{
// 后台任务是否已在系统中注册
private bool _taskRegistered = false; // 下载任务的集合
private ObservableCollection<TransferModel> _transfers = new ObservableCollection<TransferModel>(); // 所有下载任务的关联的 CancellationTokenSource 对象
private CancellationTokenSource _cancelToken = new CancellationTokenSource(); public TransferBackground()
{
this.InitializeComponent(); Init();
} private async void Init()
{
listView.ItemsSource = _transfers; // 加载存在的下载任务
await LoadDownloadAsync();
} // 加载存在的下载任务
private async Task LoadDownloadAsync()
{
IReadOnlyList<DownloadOperation> downloads = null;
try
{
// 获取存在的下载任务
downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
}
catch (Exception ex)
{
WriteLine(ex.ToString());
return;
} if (downloads.Count > )
{
List<Task> tasks = new List<Task>();
foreach (DownloadOperation download in downloads)
{
// 监视指定的后台下载任务
tasks.Add(HandleDownloadAsync(download, false));
} await Task.WhenAll(tasks);
}
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
// 遍历所有已注册的后台任务
foreach (KeyValuePair<Guid, IBackgroundTaskRegistration> task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == BackgroundTaskTransfer.TaskName)
{
_taskRegistered = true;
break;
}
} UpdateUI();
} // 新增一组(3 个)下载任务,并注册指定的后台任务
private async void btnAddDownloadAndRegister_Click(object sender, RoutedEventArgs e)
{
// 注册指定的后台任务,并返回与此后台任务相关联的 BackgroundDownloader 对象
BackgroundDownloader downloader = BackgroundTaskTransfer.RegisterBackgroundTaskAndReturnBackgrounDownloader();
_taskRegistered = true;
UpdateUI(); List<DownloadOperation> downloads = new List<DownloadOperation>();
for (int i = ; i < ; i++)
{
Uri uri = new Uri("http://files.cnblogs.com/webabcd/Windows10.rar"); 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 = downloader.CreateDownload(uri, destinationFile); downloads.Add(download);
} // 启用“组任务全部完成后触发后台任务”的功能
downloader.CompletionGroup.Enable(); WriteLine("用于完成后触发后台任务的一组下载任务创建完成了,相关的后台任务也注册了"); // 处理并监视组内的后台下载任务
Task[] tasks = new Task[downloads.Count];
for (int i = ; 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");
} // 向 lblMsg 中追加一行文本
private void WriteLine(string message)
{
var ignore = lblMsg.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
lblMsg.Text += message;
lblMsg.Text += Environment.NewLine; scrollViewer.ChangeView(, scrollViewer.ScrollableHeight, 1f);
});
} // 取消下载任务并注销指定的后台任务
private void btnRemoveDownloadAndUnregister_Click(object sender, RoutedEventArgs e)
{
// 取消下载任务
_cancelToken.Cancel();
_cancelToken.Dispose();
_cancelToken = new CancellationTokenSource(); // 注销指定的后台任务
foreach (KeyValuePair<Guid, IBackgroundTaskRegistration> task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == BackgroundTaskTransfer.TaskName)
{
// 从系统中注销指定的后台任务。唯一一个参数代表如果当前后台任务正在运行中,是否需要将其取消
task.Value.Unregister(true);
break;
}
}
_taskRegistered = false;
UpdateUI();
} private async void UpdateUI()
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
btnAddDownloadAndRegister.IsEnabled = !_taskRegistered;
btnRemoveDownloadAndUnregister.IsEnabled = _taskRegistered;
});
}
}
}

OK
[源码下载]

背水一战 Windows 10 (119) - 后台任务: 后台下载任务(任务分组,组完成后触发后台任务)的更多相关文章

  1. 背水一战 Windows 10 (120) - 后台任务: 后台上传任务

    [源码下载] 背水一战 Windows 10 (120) - 后台任务: 后台上传任务 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 后台上传任务 示例演示 uwp 的后台上 ...

  2. 背水一战 Windows 10 (118) - 后台任务: 后台下载任务(任务分组,并行或串行执行,组完成后通知)

    [源码下载] 背水一战 Windows 10 (118) - 后台任务: 后台下载任务(任务分组,并行或串行执行,组完成后通知) 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 ...

  3. 背水一战 Windows 10 (117) - 后台任务: 后台下载任务

    [源码下载] 背水一战 Windows 10 (117) - 后台任务: 后台下载任务 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 后台下载任务 示例演示 uwp 的后台下 ...

  4. 背水一战 Windows 10 (121) - 后台任务: 推送通知

    [源码下载] 背水一战 Windows 10 (121) - 后台任务: 推送通知 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 推送通知 示例演示如何接收推送通知/WebA ...

  5. 背水一战 Windows 10 (115) - 后台任务: 通过 toast 激活后台任务, 定时激活后台任务

    [源码下载] 背水一战 Windows 10 (115) - 后台任务: 通过 toast 激活后台任务, 定时激活后台任务 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 通 ...

  6. 背水一战 Windows 10 (116) - 后台任务: 前台程序激活后台任务

    [源码下载] 背水一战 Windows 10 (116) - 后台任务: 前台程序激活后台任务 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 前台程序激活后台任务 示例演示后 ...

  7. 背水一战 Windows 10 (114) - 后台任务: 后台任务的 Demo(与 app 不同进程), 后台任务的 Demo(与 app 相同进程)

    [源码下载] 背水一战 Windows 10 (114) - 后台任务: 后台任务的 Demo(与 app 不同进程), 后台任务的 Demo(与 app 相同进程) 作者:webabcd 介绍背水一 ...

  8. 背水一战 Windows 10 (67) - 控件(控件基类): DependencyObject - CoreDispatcher, 依赖属性的设置与获取, 依赖属性的变化回调

    [源码下载] 背水一战 Windows 10 (67) - 控件(控件基类): DependencyObject - CoreDispatcher, 依赖属性的设置与获取, 依赖属性的变化回调 作者: ...

  9. 背水一战 Windows 10 (59) - 控件(媒体类): Image, MediaElement

    [源码下载] 背水一战 Windows 10 (59) - 控件(媒体类): Image, MediaElement 作者:webabcd 介绍背水一战 Windows 10 之 控件(媒体类) Im ...

随机推荐

  1. vue全局API

    一.Vue.extend() 顾名思义  extend  继承,官方给出的解释是   (使用基础 Vue 构造器,创建一个“子类”.参数是一个包含组件选项的对象.) Vue构造器是指  vue是一个构 ...

  2. angular中如果几个请求相互不依赖,但是请求结果需要一起处理,可以使用

  3. 使用CSV控件方法实现参数化

    一.录制脚本 二.下面介绍如何使用CSV控件方法实现参数化 1.  添加-->配置元件-->csv Data Set Config Filename:文件的来源 Variable Name ...

  4. eclipse Maven Dependencies 黑色背景说明

    记录工作点点滴滴,大到系统设计,源码分析,小到IDE设置. 这里要说的是eclipse中Maven Dependencies 为什么有些jar用黑色背景,如下图所示: 网上很多人说jar包在本地仓库不 ...

  5. xtrabackup命令用法实战(转)

    xtrabackup命令用法实战 转载出自 https://blog.csdn.net/wfs1994/article/details/80399408 完全备份 1.创建备份 [root@linux ...

  6. 高斯混合模型(GMM) - 混合高斯回归(GMR)

    http://www.zhihuishi.com/source/2073.html 高斯模型就是用高斯概率密度函数(正态分布曲线)精确地量化事物,将一个事物分解为若干的基于高斯概率密度函数(正态分布曲 ...

  7. # 2019-2020-4 《Java 程序设计》结对项目总结

    2019-2020-4 <Java 程序设计>结对项目阶段总结---<四则运算--整数> 一.需求分析 实现一个命令行程序 要求: 自动生成小学四则运算题目(加,减,乘,除): ...

  8. 使用mobx项目开发总结(不再更新)

      mobx的优点 1,使用@observer的组件真正实现按需更新,只有监听的数据发生变化,它才会re-render,尽管父组件发生更新,但是子组件只要有@observer,则不会触发更新,类似于实 ...

  9. js 控制光标到指定位置

    js控制光标到指定节点位置(适用于富文本编辑器中) function placeCaretAtEnd(el) { //传入光标要去的jq节点对象 el.focus(); if (typeof wind ...

  10. http跳转https

    参考:http://www.cnblogs.com/tielemao/p/6386362.html webconfig中增加: <configuration> <system.web ...