For more posts in this series, see the series index.
I have written in a previous post (What do you get from being a lock screen app?) about how your background processing has a limited amount of time to do it’s processing in, what the odd unit of measurement used (the CPU second) and the overflow bucket. Even with the thinking it is hard to understand what you can accomplish in the time available, so help let’s look at what an app I built (Bing my lockscreen) does in it’s time.
In short what I hope you take away from this, is that you do get a decent amount of time and that with careful planning you can do a lot!
First off, Bing my lockscreen since it uses a timer requires lock screen permissions which means it gets at least 2 CPU seconds every fifteen minutes, plus overflow from the bucket.
First thing that happens is we get a deferral object (similar to what was explained in Async & Sharing) since we need to use async in the background task. Now we go to the RoamingSettings values to get a boolean (which needs to be cast, since RoamingSettings is a Dictionary of string & object) to see if the user has disabled automatic updates. Now assuming the user hasn’t disabled automatic updates, we connect to a web site using the HttpClient & HttpClientHandler classes and pull down some JSON as a string and convert it to an object using Windows.Data.Json.JsonObject.
Aside: if I felt too tight on CPU, I may swop to the AWESOME Json.NET, which has a better parsing performance.
If all that worked (and there is minimal logging and status checks happening in here too), I use a touch of Linq to get the first image from the IEnumerable collection that it is stored in internally. I then check the URL of the image against a value stored in LocalSettings, as I do not wish to update the lock screen with the same image multiple times. If the image is different, I download the image from Bing to the TemporaryFolder, check the file size is greater than 0. If it is, then I call the SetImageFileAsync method to change the lock screen, else I delete the damage file. Downloading is far more complex than it first appears as I need to handle proxies, handle scenarios where I can’t get the 1920x1200 resolution and need to fall back to the 1366x766 resolution image, ensure everything is written to disk BEFORE the lockscreen is set (I had some issues with I/O buffers in earlier versions that caused a minor corruption on images because they were not flushed to disk!).
Aside: Important to remember that CPU seconds, is time the CPU works – when the CPU is idle say because you are doing something I/O bound like a download, it doesn’t count!
Next I store the result of this to the log, which is just a container in LocalSettings (so that means the first time this is run we create the container too!) and store the URL of the newly changed image too. Now the process continues in a similar way for the live tile! We check if the last live tile update is the same based on the URL for the image (again comparing to a value stored in LocalSettings). I then use the TileNotification and the TileUpdateManager to send the tile update. I do not need to download the tile image in this case, as tiles do support remote URL’s for the images. Lastly I update the LocalSettings for the updated tile!
If you count the lines of code executed for this, and I am excluding blank lines, namespace declarations, comments – really just the code that actually runs it is approx. 190 lines of C# code! Far more than you may think.
I used the always amazing NDepend to generate some more stats on the assembly which does all the background processing:
- IL instructions: 2 092
- lines of code (LOC): 80
- lines of comment: 9
- Percentage Comment: 10%
- Methods: 99
- Fields: 75
- Types: 18
- Namespaces: 2
- Assembly Level: 1
- Abstractness: 0.11111
- Instability: 1
- Dist from main seq: 0.078567
- Normalized dist from main seq: 0.11111
- Relational cohesion: 2.3333
- Afferent coupling (AsmCa): 0
- Efferent coupling (AsmCe): 17
LOC is different here since NDepend uses the compiled assembly and works back, while I counted lines in Visual Studio. Same is true for methods, which NDepend works out from the compiled assembly and thus includes all anonymous methods - where if I counted that in VS, it would be 8 or 9 methods.