You can think of ScheduledTaskAgents like a mini-Windows Service. Once a ScheduledTaskAgent is registered the OS will run it every approximately every 30 minutes allowing it to do its business and you have 10 seconds to do your thing. This is done through the ScheduledTaskAgent.OnInvoke method. With my app, my ScheduledTaskAgent needed to re-fetch all of the latest traffic data, process it and update any Routes (if defined).
Within my M25.Data component I have a RefreshTrafficData method which is marked with the new async keyword. This was done to allow the WP UI to retain control while the DataManager was busy fetching and processing new data. The DataManager fires appropriate events, such as DataTransferStarted and DataRefreshed, to allow the UI to display a progress bar.
While all this works wonderfully from the WP app, calling this method from the ScheduledTaskAgent is another matter. When refreshing traffic data we don't want this to run asynchronously, since we need to take the result of the refresh and update our Live Tile with the new data.
To make a long story short, I ended up creating a new method in the DataManager which effectively was a wrapper around RefreshTrafficData, but which returned a Task object. By returning the Task object I can this call the Task.Wait method from within the ScheduledTaskAgent.OnInvoke. Execution will pause until completed and proceed to call the NotifyComplete method signalling the end of our operation.
There are a couple of extension methods which I found/created which were beneficial. The first method addresses the fact that in WP8 WebClient.DownloadString has been removed and replaced with the asynchronous DownloadStringAsync (which fires the DownloadStringComplete event with the result).
The DownloadStringTask extension method allows you to call DownloadStringAsync synchronously, if needed.
public static Task
{
var taskCompletionSource = new TaskCompletionSource
webClient.DownloadStringCompleted += (s, e) =>
{
if (e.Error != null)
{
taskCompletionSource.SetException(e.Error);
}
else
{
taskCompletionSource.SetResult(e.Result);
}
};
webClient.DownloadStringAsync(uri);
return taskCompletionSource.Task;
}
This allows you to do this:
Task
await data;
if (data.Exception == null && !string.IsNullOrEmpty(data.Result))
{
ProcessResults(data.Result);
}
Per MS documentation, the WideBackContent takes 27 characters, while the BackContent takes 13. The
GetTitleLines extension method allows you to parse your Live Tile data into lines of text to accomodate both WideBackContent and BackContent.
public static List
{
string[] parts = theString.Split(' ');
var lines = new List
string line = string.Empty;
int index = 0;
foreach (string part in parts)
{
if (!string.IsNullOrEmpty(part))
{
if (line.Length + part.Length + 1 <= wordCount)
{
line += part;
line += " ";
}
else
{
lines.Add(line);
line = part;
line += " ";
}
}
index++;
if (index >= parts.Length)
{
lines.Add(line);
}
}
return lines;
}
Here is how you can use it:
private void SetLongData(string data)
{
List lines = data.GetTitleLines(27);
if (lines.Count > 1)
{
foreach (string line in lines)
{
LongData += line + "\n";
}
}
else
{
LongData = lines[0];
}
}
private void SetMediumData(string data)
{
List lines = data.GetTitleLines(13);
if (lines.Count > 1)
{
foreach (string line in lines)
{
MediumData += line + "\n";
}
}
else
{
MediumData = lines[0];
}
}
In the next part in this series I will be wrapping things up.
No comments:
Post a Comment