作者:webabcd
介绍
重新想象 Windows 8.1 Store Apps 之后台任务的新特性
示例
1、本例用于说明 win8.1 中后台下载和上传的新特性(本例用后台下载说明,后台上传与此类似)
TransferNew.xaml
<Page x:Class="Windows81.BackgroundTask.TransferNew" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows81.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="120 0 0 0"> <ScrollViewer Height="100"> <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" /> </ScrollViewer> <Button Name="btnDownload" Content="后台下载文件" Margin="0 10 0 0" Click="btnDownload_Click" /> </StackPanel> </Grid> </Page>
TransferNew.xaml.cs
/* * 本例用于说明 win8.1 中后台下载和上传的新特性(本例用后台下载说明,后台上传与此类似) * 以下仅列出新增的功能,关于上传和下载的基础请参见:http://www.cnblogs.com/webabcd/archive/2013/10/21/3379890.html * * * BackgroundTransferGroup - 用于分组传输任务,可以指定此分组中的传输任务是并行的还是串行的 * static CreateGroup(string name) - 创建一个指定名字的传输分组,此名字需要是全系统唯一的,建议用 guid * Name - 传输分组的名字 * TransferBehavior - 传输行为(BackgroundTransferBehavior 枚举:Parallel 或 Serialized) * * BackgroundDownloader - 后台下载任务管理器 * static GetCurrentDownloadsForTransferGroupAsync(BackgroundTransferGroup group) - 获取指定传输分组下的所有下载任务(BackgroundTransferGroup 是 win8.1 新增的概念,但是仍然保留了原来 win8 里的 Group 的概念) * TransferGroup - 指定此下载任务管理器的 BackgroundTransferGroup * SuccessToastNotification - 此下载任务管理器中的所有传输任务均成功之后调用的 toast 通知 * FailureToastNotification - 有传输任务失败后调用的 toast 通知 * SuccessTileNotification - 此下载任务管理器中的所有传输任务均成功之后调用的 tile 通知 * FailureTileNotification - 有传输任务失败后调用的 tile 通知 * static RequestUnconstrainedDownloadsAsync(IEnumerable<DownloadOperation> operations) - 告诉系统,希望指定的下载任务是无约束的(返回一个 UnconstrainedTransferRequestResult 对象) * * UnconstrainedTransferRequestResult - 向系统请求无约束下载时,系统返回的结果 * IsUnconstrained - 系统是否允许你指定的下载任务是无约束的(所谓无约束就是不存在当设备依靠电池运行时通常与后台网络操作相关联的资源限制) * * DownloadOperation - 下载任务对象 * Priority - 下载任务的优先级(BackgroundTransferPriority 枚举:Default 或 High) */ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Windows.Data.Xml.Dom; using Windows.Networking.BackgroundTransfer; using Windows.Storage; using Windows.UI.Notifications; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.Web; namespace Windows81.BackgroundTask { public sealed partial class TransferNew : Page { private BackgroundTransferGroup _transferGroup; public TransferNew() { this.InitializeComponent(); } // 后台下载一个文件 private async void btnDownload_Click(object sender, RoutedEventArgs e) { // 下载地址(注意需要在 Package.appxmanifest 中增加对 .rar 类型文件的关联) Uri sourceUri = new Uri("http://files.cnblogs.com/webabcd/Windows8.rar", UriKind.Absolute); StorageFile destinationFile; try { // 保存的目标地址 destinationFile = await KnownFolders.DocumentsLibrary.CreateFileAsync("Windows8.rar", CreationCollisionOption.GenerateUniqueName); } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; return; } // 创建一个后台下载任务管理器 BackgroundDownloader backgroundDownloader = new BackgroundDownloader(); // 创建一个传输分组,并指定此分组中的传输任务是并行的还是串行的 _transferGroup = BackgroundTransferGroup.CreateGroup("webabcdBackground"); _transferGroup.TransferBehavior = BackgroundTransferBehavior.Parallel; backgroundDownloader.TransferGroup = _transferGroup; // 设置 SuccessToastNotification XmlDocument successToastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); successToastXml.GetElementsByTagName("text").Item(0).InnerText = "所有任务全部执行成功"; ToastNotification successToast = new ToastNotification(successToastXml); backgroundDownloader.SuccessToastNotification = successToast; // 设置 FailureToastNotification XmlDocument failureToastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); failureToastXml.GetElementsByTagName("text").Item(0).InnerText = "至少有一个下载任务失败了"; ToastNotification failureToast = new ToastNotification(failureToastXml); backgroundDownloader.FailureToastNotification = failureToast; // 设置 SuccessTileNotification XmlDocument successTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150Text03); XmlNodeList successTextNodes = successTileXml.GetElementsByTagName("text"); successTextNodes.Item(0).InnerText = "所有"; successTextNodes.Item(1).InnerText = "任务"; successTextNodes.Item(2).InnerText = "全部"; successTextNodes.Item(3).InnerText = "成功"; TileNotification successTile = new TileNotification(successTileXml); successTile.ExpirationTime = DateTime.Now.AddMinutes(10); backgroundDownloader.SuccessTileNotification = successTile; // 设置 FailureTileNotification XmlDocument failureTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150Text03); XmlNodeList failureTextNodes = failureTileXml.GetElementsByTagName("text"); failureTextNodes.Item(0).InnerText = "至少"; failureTextNodes.Item(1).InnerText = "一个"; failureTextNodes.Item(2).InnerText = "任务"; failureTextNodes.Item(3).InnerText = "失败"; TileNotification failureTile = new TileNotification(failureTileXml); failureTile.ExpirationTime = DateTime.Now.AddMinutes(10); backgroundDownloader.FailureTileNotification = failureTile; // 创建一个下载任务,并指定其优先级 DownloadOperation download = backgroundDownloader.CreateDownload(sourceUri, destinationFile); download.Priority = BackgroundTransferPriority.High; // 向系统请求,将指定的下载任务设置为无约束下载 List<DownloadOperation> requestOperations = new List<DownloadOperation>(); requestOperations.Add(download); UnconstrainedTransferRequestResult result = await BackgroundDownloader.RequestUnconstrainedDownloadsAsync(requestOperations); // 监视指定的后台下载任务 await HandleDownloadAsync(download); } /// <summary> /// 监视指定的后台下载任务 /// </summary> /// <param name="download">后台下载任务</param> private async Task HandleDownloadAsync(DownloadOperation download) { try { lblMsg.Text = "start"; // 当下载进度发生变化时的回调函数 Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress); // 所有下载任务的关联的 CancellationTokenSource 对象(这个最好摘出来,因为不用的时候最好手工 Dispose 掉) CancellationTokenSource _cancelToken = new CancellationTokenSource(); await download.StartAsync().AsTask(_cancelToken.Token, progressCallback); // 新增一个后台下载任务 // 下载完成后获取服务端的响应信息 ResponseInformation response = download.GetResponseInformation(); lblMsg.Text += "Completed: " + response.ActualUri + "-" + response.StatusCode.ToString(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) // 调用 CancellationTokenSource.Cancel() 后会抛出此异常 { lblMsg.Text += "Canceled: " + download.Guid; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { // 将异常转换为 WebErrorStatus 枚举,如果获取到的是 WebErrorStatus.Unknown 则说明此次异常不是涉及 web 的异常 WebErrorStatus error = BackgroundTransferError.GetStatus(ex.HResult); lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } finally { } } // 进度发生变化时,显示最新的进度信息 private void DownloadProgress(DownloadOperation download) { lblMsg.Text = download.Progress.BytesReceived.ToString("#,0") + " / " + download.Progress.TotalBytesToReceive.ToString("#,0"); } } }
2、演示如何对指定 web 资源预下载(系统会尝试在用户启动 app 之前下载指定的资源,也就是说不管 app 是否打开,系统都会在其觉得合适的时候对指定的资源做预下载)
ContentPrefetcher.xaml
<Page x:Class="Windows81.BackgroundTask.ContentPrefetcher" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows81.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="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" /> <Button Name="btnAddContentPrefetcher" Content="添加一个“预下载”任务" Click="btnAddContentPrefetcher_Click" Margin="0 10 0 0" /> <StackPanel Name="pnlStatus" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
ContentPrefetcher.xaml.cs
/* * ContentPrefetcher - 对指定 web 资源预下载(系统会尝试在用户启动 app 之前下载指定的资源,也就是说不管 app 是否打开,系统都会在其觉得合适的时候对指定的资源做预下载) * ContentUris - 需要加入到“预下载”的 uri * IndirectContentUri - 指定一个远程 xml 地址,此 xml 用于描述需要“预下载”的资源列表,格式如下 * <?xml version="1.0" encoding="utf-8"?> * <prefetchUris> * <uri>http://www.w1.com</uri> * <uri>http://www.w2.com</uri> * <uri>http://www.w3.com</uri> * </prefetchUris> * LastSuccessfulPrefetchTime - 最近一次成功地预下载的时间 */ using System; using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace Windows81.BackgroundTask { public sealed partial class ContentPrefetcher : Page { public ContentPrefetcher() { this.InitializeComponent(); UpdateStatus(); } private void btnAddContentPrefetcher_Click(object sender, RoutedEventArgs e) { // 向预下载中心增加一个预下载任务 Windows.Networking.BackgroundTransfer.ContentPrefetcher.ContentUris.Add(new Uri("http://www.baidu.com")); // 清空预下载任务 // Windows.Networking.BackgroundTransfer.ContentPrefetcher.ContentUris.Clear(); // 更新状态 UpdateStatus(); } private async void UpdateStatus() { pnlStatus.Children.Clear(); foreach (Uri uri in Windows.Networking.BackgroundTransfer.ContentPrefetcher.ContentUris) { string url = uri.ToString(); bool cached = await CheckStatus(uri); pnlStatus.Children.Add(new TextBlock() { Text = url + " - " + cached.ToString() }); } DateTimeOffset? lastPrefetchTime = Windows.Networking.BackgroundTransfer.ContentPrefetcher.LastSuccessfulPrefetchTime; if (lastPrefetchTime != null) { lblMsg.Text = "最近一次成功地“预下载”了内容的时间是:" + lastPrefetchTime.Value.ToString("yyyy-MM-dd hh:mm:ss"); } else { lblMsg.Text = "还没有“预下载”成功过"; } } private async Task<bool> CheckStatus(Uri uri) { // 只从本地缓存加载指定的 http 资源 var filter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter(); filter.CacheControl.ReadBehavior = Windows.Web.Http.Filters.HttpCacheReadBehavior.OnlyFromCache; var httpClient = new Windows.Web.Http.HttpClient(filter); var request = new Windows.Web.Http.HttpRequestMessage(Windows.Web.Http.HttpMethod.Get, uri); try { // 指定的资源在本地有缓存 await httpClient.SendRequestAsync(request); return true; } catch { // 指定的资源在本地无缓存 return false; } } } }
3、演示后台任务的其它新特性
Other.xaml
<Page x:Class="Windows81.BackgroundTask.Other" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows81.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="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" Margin="0 10 0 0" Text="详见 .cs 中的代码注释" /> </StackPanel> </Grid> </Page>
Other.xaml.cs
/* * win 8.1 中与后台任务相关的其它新特性: * 1、在“设置”->“搜索和应用”->“通知”->“免打扰时间”中可以设置免打扰时间 * 2、免打扰时间内,所有后台任务均暂停,免打扰时间过后,后台任务将随机在不同时间点启动 * 3、免打扰时间允许两种例外:来电和闹钟 * 4、后台任务 IBackgroundTaskInstance.Canceled 如果 5 秒内没有完成,则系统会终止该应用,并生成错误报告上传至 windows 商店的开发人员账户 * 5、Windows.ApplicationModel.Background.BackgroundWorkCost.CurrentBackgroundWorkCost 当前后台任务的开销(BackgroundWorkCostValue 枚举:Low, Medium, High) * 6、IBackgroundTaskInstance.SuspendedCount - 由资源管理政策导致后台任务挂起的次数(win8 就支持,之前忘了写了) * * * 关于后台任务的基础请参见:http://www.cnblogs.com/webabcd/archive/2013/10/15/3369581.html */ using Windows.ApplicationModel.Background; using Windows.UI.Xaml.Controls; namespace Windows81.BackgroundTask { public sealed partial class Other : Page { public Other() { this.InitializeComponent(); } } }
OK
[源码下载]