转载

【Win10 UWP】后台任务与动态磁贴

动态磁贴(Live Tile)是WP系统的大亮点之一,一直以来受到广大用户的喜爱。这一讲主要研究如何在UWP应用里通过后台任务添加和使用动态磁贴功能。

从WP7到Win8,再到Win10 UWP,磁贴模板不断进行调整和优化,目前磁贴模板已经发展到第三代,一般称之为“ Adaptive Tile Templates ”。

在运用UWP动态磁贴之前,请先了解一下自适应磁贴的语法规则。关于自适应磁贴模板的语法规则,请详读这篇文章: http://blogs.msdn.com/b/tiles_and_toasts/archive/2015/06/30/adaptive-tile-templates-schema-and-documentation.aspx

一. 磁贴更新的原理

磁贴的“动态”,在于它能不断地进行更新,展示新的内容。磁贴又可分为主磁贴(即由应用列表Pin到桌面的磁贴)和二级磁贴(即由应用内部通过程序控制Pin到桌面的磁贴)。这两种磁贴都支持小、中、宽和大磁贴4种尺寸。

Windows.UI.Notifications.TileUpdater 可以用来管理和修改当前磁贴的内容,从而达到更新磁贴的目的。TileUpdater对象必须通过Windows.UI.Notifications.TileUpdateManager的 CreateTileUpdaterForApplicationCreateTileUpdaterForSecondaryTile 方法来获取。如:

1 var updater = TileUpdateManager.CreateTileUpdaterForApplication();
1 var updater = TileUpdateManager.CreateTileUpdaterForSecondaryTile("appdota2");

然后调用updater的Update方法即可实现对内容的更新。Update方法需要传入一个TileNotification对象:

1 // 2 // 摘要: 3 //     将内容或外观的更改应用于图块。 4 // 5 // 参数: 6 //   notification: 7 //     为平铺的内容提供新的 XML 定义的对象。 8 public void Update(TileNotification notification);

顾名思义,TileNotifiction承载着要进行通知的磁贴模板,磁贴模板实际上就是一个定义好的XML文件。在UWP里,这个磁贴模板就需要按照“Adaptive Tile Templates”的规则来定义。

二.磁贴的更新方式

磁贴更新的方式可以通过程序内部控制,如在后台请求新数据并进行更新,这种方式也就是通过后台任务(Background Task)来实现更新;也可以通过推送通知使磁贴产生变化。我们今天重点讲后台任务的更新磁贴方法。

后台任务更新逻辑:

1.应用首先注册一个后台任务

2.后台任务定期向服务器请求新数据

3.服务器传回新的数据

4.后台任务通过TileUpdater更新磁贴内容

【Win10 UWP】后台任务与动态磁贴

三.实现后台任务更新磁贴

1.后台任务逻辑

创建一个WinRT组件,再创建一个类叫LiveTileTask,并且实现IBackgroundTask接口,必须实现接口的Run方法:

1  public sealed class LiveTileTask : IBackgroundTask  2 {  3     public async void Run(IBackgroundTaskInstance taskInstance)  4     {  5         var deferral = taskInstance.GetDeferral();  6         // TODO: 获取数据,更新磁贴逻辑  8         deferral.Complete(); 10     } 11 }

Run方法中必须获取deferral对象,并且执行完成后需要调用deferral对象关闭,因为我们在后台任务中是需要执行异步代码的,所以 获取完deferral对象之后,大约有5秒钟的时间可以进行异步操作,超过时间系统就会强制释放deferral对象 。这样能保证较好的用户体验,如果异步请求的时间过长,自然会认为执行失败而不会去更新磁贴了。

接下来再Run方法中间位置开始执行请求数据的任务:

1 public async void Run(IBackgroundTaskInstance taskInstance)  2 {  3     var deferral = taskInstance.GetDeferral();  4   5     await GetLatestNews();  6   7     deferral.Complete();  8 }  9  10 private IAsyncOperation<string> GetLatestNews() 11 { 12     try 13     { 14         return AsyncInfo.Run(token => GetNews()); 15     } 16     catch (Exception) 17     { 18         // ignored 19     } 20     return null; 21 }

其中GetNews方法即向服务端请求数据,完成后可以开始更新磁贴:

1 private async Task<string> GetNews()  2 {  3     try  4     {  5         var response = await ApiService.GetHotNewsListAsync();  6         if (response?.Data != null)  7         {  8             var news = response.Data.Take(5).ToList();  9             UpdatePrimaryTile(news); 10             UpdateSecondaryTile(news); 11         } 12  13     } 14     catch (Exception) 15     { 16         // ignored 17     } 18     return null; 19 }

注意磁贴最多只能更新5个,所以只处理返回数据的前5个。

更新磁贴的方法非常简单:

1 private void UpdatePrimaryTile(List<News> news)  2 {  3     if (news == null || !news.Any())  4     {  5         return;  6     }  7   8     try  9     { 10         var updater = TileUpdateManager.CreateTileUpdaterForApplication(); 11         updater.EnableNotificationQueueForWide310x150(true); 12         updater.EnableNotificationQueueForSquare150x150(true); 13         updater.EnableNotificationQueueForSquare310x310(true); 14         updater.EnableNotificationQueue(true); 15         updater.Clear(); 16  17         foreach (var n in news) 18         { 19             var doc = new XmlDocument(); 20             var xml = string.Format(TileTemplateXml, n.Pic, n.Title, n.Desc); 21             doc.LoadXml(WebUtility.HtmlDecode(xml), new XmlLoadSettings 22             { 23                 ProhibitDtd = false, 24                 ValidateOnParse = false, 25                 ElementContentWhiteSpace = false, 26                 ResolveExternals = false 27             }); 28  29             updater.Update(new TileNotification(doc)); 30          } 31    } 32    catch (Exception) 33    { 34        // ignored 35    } 36 }

我们采用队列的形式允许磁贴逐个更新,还有一个需要注意的地方,服务端返回的数据可能带有转义字符,在加载模板XML的时候必须做一下编码处理,否则可能导致异常而无法更新磁贴。当然其中的TileTemplateXml自己根据自适应磁贴的规则定义即可,举例:

1 private const string TileTemplateXml = @"  2 <tile branding='name'>   3   <visual version='3'>  4     <binding template='TileMedium'>  5       <image src='{0}' placement='peek'/>  6       <text>{1}</text>  7       <text hint-style='captionsubtle' hint-wrap='true'>{2}</text>  8     </binding>  9     <binding template='TileWide'> 10       <image src='{0}' placement='peek'/> 11       <text>{1}</text> 12       <text hint-style='captionsubtle' hint-wrap='true'>{2}</text> 13     </binding> 14     <binding template='TileLarge'> 15       <image src='{0}' placement='peek'/> 16       <text>{1}</text> 17       <text hint-style='captionsubtle' hint-wrap='true'>{2}</text> 18     </binding> 19   </visual> 20 </tile>";

这个模板实现的效果是如同商店应用的Peek动态更新效果:

Animated Peek shown Peek sliding up Content shown Peek sliding down
【Win10 UWP】后台任务与动态磁贴 【Win10 UWP】后台任务与动态磁贴 【Win10 UWP】后台任务与动态磁贴 【Win10 UWP】后台任务与动态磁贴 【Win10 UWP】后台任务与动态磁贴

2.注册后台任务

注意后台任务必须在前台程序进行触发(Trigger)设置,即所谓的注册后台任务。后台任务将根据触发器是否被触发而执行。

Win8.1的Trigger有 SystemTrigger, TimeTrigger, MaintenaceTrigger, DeviceUseTrigger, DeviceServingTrigger, PushNotificationTrigger

WP8.1的Trigger有 CachedFileUpdaterTrigger, DeviceConnectionChangedTrigger, GattCharacteristicNotificationTrigger, RfcommonConnectionTrigger, LocationTrigger

Win10新增了如下Trigger: AppointmentStoreNotificationTrigger, ContactStoreNotificationTrigger, BluetoothLEAdvertisementWarcherTrigger, BluetoothLEAdvertisementPublisherTrigger, DeviceWatcherTrigger, ActivitySensorTrigger, SensorDataThresholdTrigger, ToastNotificationHistoryChangedTrigger, ToastNotificationActionTrigger, ApplicationTrigger, SocketActivityTrigger

如果我们要进行周期性的磁贴更新,那么我们可以将用Timer触发器去进行触发,需要在Package.appxmanifest中声明一个后台任务,支持的任务类型勾选计时器,且应用设置中Entry Point设置为LiveTileTask的完整类名。  

【Win10 UWP】后台任务与动态磁贴

在前台程序的App.cs或其他地方进行设置:

1 private const string LIVETILETASK = "LIVETILETAKS";  2 private async void RegisterLiveTileTask()  3 {  4     var status = await BackgroundExecutionManager.RequestAccessAsync();  5     if (status == BackgroundAccessStatus.Unspecified || status == BackgroundAccessStatus.Denied)  6     {  7         return;  8     }  9     BackgroundTaskRegistration.AllTasks.ForEach(t => 10     { 11         if (t.Value.Name == LIVETILETASK) 12         { 13             t.Value.Unregister(true); 14         } 15     }); 16  17     var taskBuilder = new BackgroundTaskBuilder 18     { 19          Name = LIVETILETASK, 20          TaskEntryPoint = typeof(LiveTileTask).FullName 21     }; 22     taskBuilder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable)); 23  24     var updater = TileUpdateManager.CreateTileUpdaterForApplication(); 25     updater.Clear(); 26     var updater2 = TileUpdateManager.CreateTileUpdaterForSecondaryTile("appdota2"); 27     updater2.Clear();33  34     taskBuilder.SetTrigger(new TimeTrigger(60, false)); 35     taskBuilder.Register(); 36 }

对于后台任务,还可以设定一定的条件使其触发,如当没有网络的情况下,即使到了时间周期,也不会去触发后台任务。

这样就实行了每个60分钟触发一次后台任务,让后台任务去请求新的数据,并将磁贴已队列的形式循环进行更新。

正文到此结束
Loading...