【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.aspxapp

一. 磁貼更新的原理

磁貼的「動態」,在於它能不斷地進行更新,展現新的內容。磁貼又可分爲主磁貼(即由應用列表Pin到桌面的磁貼)和二級磁貼(即由應用內部經過程序控制Pin到桌面的磁貼)。這兩種磁貼都支持小、中、寬和大磁貼4種尺寸。異步

Windows.UI.Notifications.TileUpdater能夠用來管理和修改當前磁貼的內容,從而達到更新磁貼的目的。TileUpdater對象必須經過Windows.UI.Notifications.TileUpdateManager的CreateTileUpdaterForApplicationCreateTileUpdaterForSecondaryTile 方法來獲取。如:async

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

而後調用updater的Update方法便可實現對內容的更新。Update方法須要傳入一個TileNotification對象:ide

1 //
2 // 摘要:
3 //     將內容或外觀的更改應用於圖塊。
4 //
5 // 參數:
6 //   notification:
7 //     爲平鋪的內容提供新的 XML 定義的對象。
8 public void Update(TileNotification notification);

顧名思義,TileNotifiction承載着要進行通知的磁貼模板,磁貼模板實際上就是一個定義好的XML文件。在UWP裏,這個磁貼模板就須要按照「Adaptive Tile Templates」的規則來定義。優化

 

二.磁貼的更新方式

磁貼更新的方式能夠經過程序內部控制,如在後臺請求新數據並進行更新,這種方式也就是經過後臺任務(Background Task)來實現更新;也能夠經過推送通知使磁貼產生變化。咱們今天重點講後臺任務的更新磁貼方法。ui

 

後臺任務更新邏輯:編碼

1.應用首先註冊一個後臺任務spa

2.後臺任務按期向服務器請求新數據

3.服務器傳回新的數據

4.後臺任務經過TileUpdater更新磁貼內容

三.實現後臺任務更新磁貼

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


 

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的完整類名。  

在前臺程序的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分鐘觸發一次後臺任務,讓後臺任務去請求新的數據,並將磁貼已隊列的形式循環進行更新。

相關文章
相關標籤/搜索