[源码下载]

背水一战 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. 天坑!c++调用python,遭遇R6034问题

    起源: AllMyTube下载核心,是c#组件调用c++dll,在dll中初始化Python运行环境.在工作目录有msvcr90.dll文件时,程序运行会弹出如下错误: R6034. -------- ...

  2. Java并发编程:深入剖析ThreadLocal(转)

    目录大纲: 一.对ThreadLocal的理解 二.深入解析ThreadLocal类 三.ThreadLocal的应用场景 原文链接:http://www.cnblogs.com/dolphin052 ...

  3. ios 根据 schemes 打开 app

    公司出需求,要让 h5链接直接打开用户的 app,如果没有安装 app 直接跳转到 appStore 这就需要给 app 配置 schemes 即可 1.在Info.plist中 LSApplicat ...

  4. node.js中对 mysql 进行增删改查等操作和async,await处理

    要对mysql进行操作,我们需要安装一个mysql的库. 一.安装mysql库 npm install mysql --save 二.对mysql进行简单查询操作 const mysql = requ ...

  5. http 6000端口 chrome ERR_UNSAFE_PORT

    参考原因 折腾了半天.chrome的提示真不够意思,还是火狐的提示厉害,知道真相的我眼泪流下来.

  6. iserver频繁崩溃、内存溢出事故解决小记

    一.事故分析 在生产项目中,频繁遇到iserver隔一段时间就会出现崩溃的情况. 将iserver错误日志发给技术客服后,说是内存溢出的问题. 查看服务器的配置是32g内存,按理说不该出现此类问题. ...

  7. 初学html,任务1:一个简单html页面,要求:内容页面装一篇文章 用html来分段

    这是主要内容部分,用html实现版块分布. 接下来是样式部分. 让页面所有元素的padding和margin都设置为0 : 否则加入一张大的覆盖的背景图片后,会由于浏览器的缘故,图片周边有白边: 设置 ...

  8. Numpy 数据类型

    numpy支持的数据类型比Python内置的类型多很多,基本上可以和C语言的数据类型对应上, 其中部分类型对应为Python内置的类型.下表列举了常用的Numpy基本类型. 名称 描述 bool_ 布 ...

  9. Spring Boot 异常处理

    Spring Boot 异常处理 本节介绍一下 Spring Boot 启动时是如何处理异常的?核心类是 SpringBootExceptionReporter 和 SpringBootExcepti ...

  10. 刷shipid 简便方法

    将表中的数据手动更改: select * from cmpps025 where  pino = ''; insert into cmpps025 select ncmp, pino, pono, i ...