【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更新磁貼內容

三.實現后台任務更新磁貼

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分鍾觸發一次后台任務,讓后台任務去請求新的數據,並將磁貼已隊列的形式循環進行更新。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM