My previous post about Live Tiles showed you how to update the back side of the Application Tile inside an application. The big drawback of this approach is that your tile will only be updated as a result of your application being executed. Depending on the information you want to display on your tile, you might want to take another approach to update your application’s tiles.
This time we will make use of a Background Agent to periodically update the Application Tile without the need for the application to actually run. We again work on the same sample application, Clicker, and extend its functionality by adding a PeriodicTask. A PeriodicTask is supposed to be lightweight and will run at fixed intervals. A PeriodicTask will not run forever, but it expires after at most 14 days. From within the running application, the PeriodicTask can be renewed before the expiration date. In this way, applications that are not used will not continue to use precious device resources through a PeriodicTask forever.
Back to our tile though. The scenario we want to implement is the following. The backside of the tile already displays the high score. It would be nice to alternate between showing the game’s high score and showing the amount of time the game has not been played. To implement this functionality, a PeriodicTask is ideal.
In order to make use of a PeriodicTask, a new project of type Windows Phone Scheduled Task Agent must be added to the solution containing the Clicker application. This newly created project already contains an OnInvoke method that is called each time the PeriodicTask executes. Inside the OnInvoke method, the back side of the live tile is updated. The PeriodicTask will execute approximately once every 30 minutes. It is important to determine how data can be exchanged between the application and the PeriodicTask. The PeriodicTask has access to the application’s IsolatedStorage. In Clicker, a file is used to save the high score, which in turn is read by the PeriodicTask. Since the OnInvoke method will execute on a thread and will be terminated after being executed, data needed for the PeriodicTask must also be persisted in IsolatedStorage. To store and retrieve high scores, a static class is available inside the Clicker application. This class will be used in the application to store high scores. The same class will also be used in the PeriodicTask to retrieve high scores. Together with the high score, also a boolean variable is stored, indicating if the high score or an informational message should be stored on the back of the Application Tile.
- public static string GetLiveTileBack()
- {
- string highScore = string.Empty;
- bool showHighScore;
- using (var store = IsolatedStorageFile.GetUserStoreForApplication())
- {
- mtx.WaitOne();
- if (store.FileExists(highScoreFileName))
- {
- string storedHighScore;
- using (var highScoreStream = new StreamReader(store.OpenFile(highScoreFileName, FileMode.Open)))
- {
- showHighScore = Convert.ToBoolean(highScoreStream.ReadLine());
- storedHighScore = highScoreStream.ReadToEnd();
- }
- if (showHighScore)
- {
- highScore = storedHighScore;
- }
- using (var highScoreStream = new StreamWriter(store.CreateFile(highScoreFileName)))
- {
- showHighScore = !showHighScore;
- highScoreStream.WriteLine(showHighScore.ToString());
- highScoreStream.WriteLine(storedHighScore.ToString());
- }
- }
- mtx.ReleaseMutex();
- }
- return highScore;
- }
The following code shows how the PeriodicTask is created / renewed and scheduled to update the Live Tile of the application.
- public static void PeriodicTileUpdater()
- {
- PeriodicTask periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
- if (periodicTask != null)
- {
- RemoveAgent(periodicTaskName);
- }
- periodicTask = new PeriodicTask(periodicTaskName)
- {
- Description = "Periodic Task to Update the ApplicationTile"
- };
- // Place the call to Add in a try block in case the user has disabled agents.
- try
- {
- ScheduledActionService.Add(periodicTask);
- // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
- #if DEBUG_TASK
- ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(30));
- #endif
- }
- catch (InvalidOperationException)
- {
- }
- }
To make debugging easier, the PeriodicTask will execute every 30 seconds in debug mode, instead of every 30 minutes. The other important piece of functionality is in the PeriodicTask itself. The OnInvoke method is started every 30 minutes (every 30 seconds in debug mode). It retrieves the information to be displayed on the back of the Application Tile.
- protected override void OnInvoke(ScheduledTask task)
- {
- if (task.Name.Contains(clickerTaskName))
- {
- // Application Tile is always the first Tile, even if it is not pinned to Start.
- ShellTile TileToFind = ShellTile.ActiveTiles.First();
- if (TileToFind != null)
- {
- string backContent = LiveTile.GetLiveTileBack();
- if (backContent == string.Empty)
- {
- backContent = "Isn't it time to play again";
- }
- StandardTileData NewTileData = new StandardTileData
- {
- BackContent = backContent
- };
- TileToFind.Update(NewTileData);
- }
- #if DEBUG_TASK
- ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(30));
- #endif
- }
- NotifyComplete();
- }
At the end of the OnInvoke method you will see a call to NotifyComplete. This call is important because it releases resources of the PeriodicTask. Omitting to do this means the PeriodicTask will not be rescheduled to run again after the specified interval in debug mode or after the fixed interval in release mode.