At this point, our AroundMe app is missing just one final feature: the ability to randomly
change the lock screen to one of the pictures we've selected every few moments, even
if the AroundMe app isn't currently running.
This functionality requires we use a Scheduled Task Agent. The Scheduled Task Agent
runs in the background and executes at specific intervals even when our program is not
in the foreground -- in other words, when it is not the app you're working with on screen.
In our case, we'll schedule the task to run every 30 seconds. When the scheduled task
executes, it will call into our LockScreenHelper class'
SetRandomImageFromLocalStorage() method.
To enable this functionality we will add a new Windows Phone Scheduled Task Agent
project to our current solution. This poses a problem ... we will need to execute the code
in our LockscreenHelper.cs from our new project. However, currently this file (as well as
its dependencies, the FlickrImages.cs and Photo.cs class files) reside in our main
project at the moment. We could merely cut, copy and paste the code files so that they
reside in both projects, however the better solution (from a code maintenance
perspective) would be to create a third project, a Windows Phone Class Library project,
that will house the shared code files, then reference it from both projects. We typically
want to share code rather than duplicate it so that any changes or updates can be made
in one place and shared by all dependents.
Our game plan:
1. We'l add a new Windows Phone Scheduled Task Agent project to our Solution
2. We'l add a couple lines of code that wil call the SetRandomImageFromLocalStorage() method and allow us to test it in the emulator
3. We'l add a new Windows Phone Class Library project in our solution and wil move our three class files into it that wil be shared between the other two projects.
4. We'l move those code files into the Windows Phone Class Library, we'l use NuGet to add in Newtonsoft's JSON library and the Microsoft.Net.Http library package, we'l clean up the namespaces and so on.
5. We'l create references from the AroundMe project and the Scheduled Task Agent project to our new Class Library project.
6. In the main AroundMe project, I'l need to introduce the Scheduled Task Agent to the operating system by launching it when AroundMe starts up, and configure the WMAppManifest.xml to be an extension in order to allow our app to be a Lock screen Background Provider.
7. And if all goes well, we'll run it and watch the fireworks.
8. I'm not going to submit this app to the store ... we've already seen that process in lesson 23, but I wil show you some last steps required before submitting the app to the store,including how to get a token to properly license and use Map Services in your Phone Store app.
1. Add a new Windows Phone Scheduled Task Agent project to our solution called
AroundMe.Scheduler
I don't believe I've ever demonstrated how to add a second project to a solution in the
C# Fundamentals series. Assuming you do not know how to do this...
1. Right-click the solution name in the Solution Explorer
2. Select Add
3. New Project ...
The Add New Project dialog appears:
1. Select the Visual C# Windows Phone templates
2. Select Windows Phone Scheduled Task Agent project template
3. Name it: AroundMe.Scheduler ... we'l use a common notation when naming projects, the dot notation, to match what we want the default namespace for our code in this project to have
4. Click OK
You should now see a second project in the Solution Explorer. The most important part
of this project is the ScheduledAgent.cs class.
Most of code in the ScheduledAgent.cs file is "boiler plate" meaning we will not need to
change it, but it must be there in order to create a valid Scheduled Task Agent.
However, if you scroll down near the bottom of the file, in line 41 you'll find the
OnInvoke() method with a TODO comment. This is where we'll implement our code as
follows:
As you can see, I added a TODO. We'll come back here later and add code that will set
the random image from local storage. We have a little setup work to do first.
Before we do that, I've added one line of code (with about 10 lines of comments) that
explain it's purpose. I copied it from this source:
So, line 61 of the code snippet above:
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(30));
... is merely for testing purposes in the Phone Emulator. This allows developers to
watch the behavior of the task every 30 seconds. Otherwise, as we'll learn in a moment,
when our app is deployed to a real Phone device, it will only run once every 20 to 40
minutes. More about that in just a moment.
As the comments above it explain, we need to either remove this line of code, or at least
wrap it in an if statement like this:
[code language="csharp"]if (Debugger.IsAttached) {
}[/code]
The original article references this additional background information:
That article is very important for our purposes, and if you think you need to do some
background processing or scheduled processing, you MUST read that article. In a
nutshell, there are two types of background tasks: ResourceIntensiveTasks and
PeriodicTasks. Resource Intensive tasks run in the background because they may take
a long time to complete ... they're processor intensive. PeriodicTasks run at set
intervals. In our case, we're creating a PeriodicTask.
There are different rules and constraints depending on the type of background task
we're working with. Both ResourceIntensiveTasks and PeriodicTasks share the
following constraints:
1. An application may have only one background agent, and only one instance of the agent runs at a time.
2. We're limited by which of the Phone's APIs we can utilize from a background task
3. We're limited by the amount of the phone's resources we can use ... no more than 11 megabytes for an application like ours
4. Our scheduled task will only work for two weeks before it needs to be rescheduled by re-running the AroundMe app
5. If our app crashes twice in a row, it wil be unscheduled by the operating system. In our case, since we're working with PeriodicTasks, we need to be aware of a few additional things:
6. Our task wil run about every 30 minutes, however the phone's operating system decides when to run the tasks. In fact, it might run ALL scheduled tasks at the same time because it's more efficient to use the phone's battery once every 30 minutes than a bunch of times during that same period for each individual Scheduled Task that wants to run. So, that 30 minute time frame can drift as much as 10 minutes in either direction. So, when we deploy our AroundMe app to a phone, it will change image every 20 to 40 minutes or so.
7. Our Scheduled task can only run for 25 seconds before it is shut down by the operating system. That shouldn't affect us, but we should be aware of that for future reference.
8. If the battery is low and the phone goes into Battery Saver mode, then our Scheduled. Task may not run at all. If you have a Windows Phone 8 and your batter gets low, you'l see a little heart icon over the battery meaning that it has shut down scheduled tasks, turned off some of the functionality of the phone like the GPS and Bluetooth radio and more. Users can configure how aggressive they want Battery Saver to be in the Settings screen. I have a battery monitoring app on my start page, and I was getting low on battery, then I noticed that Battery Saver mode kicked in and I had 20 hours left. That's because it was throttling what was going on in the background. So, be aware of that when you create your own Scheduled Tasks.
9. Final y, depending on each phone's configuration, there may be a limit to how many background agents can run at the same time. A few days ago I hit that limit and it asked me to choose some background tasks to shut down because the phone was low on memory.
So, if you plan on submitting a phone to the Store you'll need to be willing to support
users. If you want to support your users, you'll need to know a little about how the
phone and the operating system work with your app. If the user complains that their
background is not changing often enough, you might want to ask a few questions about
how many apps are currently running.
To see what's running in the background, go to your Windows Phone's settings, swipe
over to the applications page. There you will find "background tasks" setting at the top
of the list. Once inside the settings, you'll see a list of all the apps running in the
background. To shut it down, just tap on the app and tap the "block" button. To confirm
that you've shut down the app, press and hold the back button to view any open apps.
At any rate, that article also does a nice job of explaining the general lifecycle of a
background agents. From the article:
"When the agent is launched, the operating system calls OnInvoke(ScheduledTask) ...
When the agent has completed its task, it should call NotifyComplete() or Abort() to let
the operating system know that it has completed. NotifyComplete should be used if thetask was successful. If the agent is unable to perform its task – such as a needed server being unavailable - the agent should call Abort, which causes the IsScheduled property to be set to false."
So that is the purpose of the NotifyComplete() in line 63 of the code snippet above.
Again, we'll come back and revisit the TODO comment in line 45 after we've moved our
class files from our main AroundMe project to a new Windows Phone Class Library
project, which we'll do next.
2. Create a new Phone Class Library called AroundMe.Core and move class files to it
Just like in step 1, we'll add a new project to our solution. So, right-click the our solution
entry in the Solution Explorer, select Add, New Project ... In the Add New Project dialog...
1. Select Visual C# Windows Phone templates
2. Select the Windows Phone Class Library project template
3. Rename to AroundMe.Core ... again, we're following a naming convention that will produce a namespace that we want to use in our solution. The term "Core" indicates essential functionality that wil be utilized in other projects
4. Click OK
Your Solution Explorer should look like this, with one solution and three projects:
NOTE: I deleted the Class.cs file from the new AroundMe.Core project. We won't need
it. Selected it and hit the delete key on your keyboard.
Next, I'll drag my three class files:
· FlickrImage.cs
· LockScreenHelper.cs
· Photo.cs
... from the AroundMe project to the AroundMe.Core project. THIS WILL MERELY
COPY THE FILES ... you now have two copies of the files.
In the AroundMe project, select those three files and delete them. Those three files
should only appear in your AroundMe.Core project:
Next, since these files depend on third-party or optional packages, we'll need to use
NuGet to import the into our new AroundMe.Core project.
If it's not already open in Visual Studio, go to the Tools menu, select Library Package
Manager menu option, then the Package Manager Console sub menu.
In the Package Manager Console:
1. Make sure the Default project is set to: AroundMe.Core
2. At the Package Manager prompt, type: install-package Newtonsoft.Json
3. Ht enter.
Assuming that completes successfully ...
1. At the Package Manager prompt, type: install-package Microsoft.Net.Http
2. Ht enter.
We've talked about both of these before, so I'll not review that now. These are simply
dependencies that the class files we moved will need. Confirm everything installed
correctly in the Solution Explorer:
... your references should match what I have in the AroundMe.Core project.
Just to make sure everything works IN THAT PROJECT, right-click the AroundMe.Core
project name in the Solution Explorer and select Build from the context menu:
... assuming everything builds without any errors, we're ready to reference our new
Windows Phone Class Library project from our main AroundMe project and the
Background Task Agent project.
In the Solution Explorer, in the AroundMe project, add a reference to the
AroundMe.Core project
3. Add reference from AroundMe to AroundMe.Core and fix any lingering problems from the refactoring
1. Right-click References
2. Select Add Reference ...
When the Reference Manager dialog appears ...
1. Navigate to the Solution | Projects tab
2. Place a check-mark next to AroundMe.Core
3. Click OK
Now it's time to clean up our AroundMe project's source code. When you move things
from one project to another, there's bound to be some problems. In our case, we need
to comb through our code and make sure our AroundMe project can find the same code
in the new AroundMe.Core Class Library project.
In the SearchResults.xaml.cs class, in the appBarButton_Click click event, I find my first
problem ... I hover my mouse cursor over the blue squiggly line:
... and it tells me: "AroundMe.LockScreenHelpers is inaccessible due to its protection
level".
When I see the term "protection level", that usually means a visibility problem ... in other
words, our old code can't "see" our new code. And usually that's because I'm missing
an accessibility modifier in the new code.
In my LockScreenHelpers.cs file I add the public accessibility modifier to the class
declaration:
At this point you may need to re-build the AroundMe.Core project in order for the
warnings in the SearchResults.xaml.cs class to go away.
But now that I'm looking at my LockScreenHelpers.cs class, I see something else ... I've
moved these three files to a new project, and named the project deliberately to be in a
different namespace. However, since I merely copied those files, the declared
namespace remains as it did before ... simply:
[code language="csharp"]namespace AroundMe { }[/code]
But do I want the namespace to be AroundMe? I think I would prefer it to match the
project name, AroundMe.Core ... so I make that change to each of the class files in my
AroundMe.Core project:
After I make that change, I Build the AroundMe.Core project again.
Now, I'll need to add a using statement to the SearchResults.xaml.cs file so that the
compiler knows where to look for the LockScreenHelpers class:
By simply adding the:
using AroundMe.Core;
... to my using statements, that should fix any namespace problems I introduced with
this change.
4. Create a reference from AroundMe.Scheduler to AroundMe.Core and write code to set background lock screen image In the Solution Explorer:
1. Right-click the References entry of associated with the AroundMe.Scheduler project
2. Select Add Reference...
That will display the Reference Manager dialog ...
1. Select the Solution | Projects tab
2. Place a check-mark in the box next the to AroundMe.Core project
3. Click OK
Next, add the following using statement to the ScheduledAgent.cs file:
using AroundMe.Core
1. Add this line of code at the top, removing the TODO comments: ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(30));
2. Add the async modifier to the method declaration. We've talked about this at length in lesson 27.
5. Modify the AroundMe project's App.xaml.cs to introduce the background scheduled
task agent to the operating system Finally, we'll need to register our new background scheduled task agent with the operating system. We'll do this when our AroundMe app first starts up, so we'll be adding the code to the App.xaml.cs which handles the Application_Launching event.
In the App.xaml.cs file, add the following code to the Application_Launching event
handler method stub:
There are two basic activities in this code passage:
· Lines 65 - 72 -- Here we are trying to find any running versions of our background scheduled task agent. If it is running, we'l want to remove it from the ScheduledActionService which acts as a registrar for the operating system's scheduled tasks.
· Lines 83 - 90 -- Here we create an instance of our background scheduled task agent and register it with the ScheduledActionService.
Why do we try to remove it, then re-add it? Keep in mind what we said earlier ... after 2
weeks, our scheduled tasks will be de-scheduled. This little dance simply keeps our
background scheduled task agent scheduled and operating for another two weeks.
Notice that we set the task's Description property in line 84 ... this will be displayed to
the end user if they look at the background tasks in Settings at the very bottom of the
page where you can choose to block the app. It's a good reminder to the user of what
this particular background scheduled task agent is doing ... in case they've forgotten
that they installed your app.
· In line 88 I'm creating this LaunchForTest() method like I did in the AroundMe.Scheduler to schedule the background task for its first execution for development purposes in the Phone Emulator.
· Lines 74 - 81 -- this is a reminder to me and you ... we'l need to open up the WMAppManifest.xml file and add this ExtendedTask element.
We've already done this once or twice, but just to refresh your memory:
1. In the Properties section / folder ...
2. Right-click on the WMAppManifest.xml file ...
3. Choose Open With ... from the menu
From the Open With dialog:
1. Select XML (Text) Editor with Encoding
2. Click OK
You will be asked about the "encoding" part in the next dialog:
Simply leave the "auto-detect" option and click OK.
We'll want to make changes INSIDE of the <Tasks> element, below the <DefaultTask>
element like so:
We'll add the following XML:
<ExtendedTask Name="AroundMeLockScreenChangerTask">
<BackgroundServiceAgent Specifier="ScheduledTaskAgent"
Name="AroundMeLockScreenChanger" Source="AroundMe.Scheduler"
Type="AroundMe.Scheduler.ScheduledAgent" />
</ExtendedTask>
What does this do? Per this web page: http://msdn.microsoft.com/en-
... in the section titled "ExtendedTasks Element" about mid-way through, it says:
"The ExtendedTasks element is a child of the Tasks element and contains
BackgroundServiceAgent elements. This element defines the use of multiple tasks by
an app. Extended tasks are named by the developer. Currently, you can use the
ExtendedTasks element only to define background tasks."
Remember, the WMAppManifest.xml is there mostly for certification purposes into the
Store, and is used to introduce your app to the phone's operating system and allow it to
become part of the phone's ecosystem. Here's a great example ... we tell the phone's
operating system that we want this part of our app, the AroundMe.Scheduler to be
included with the other background task agents that are currently running. We're
registering with the background task agent scheduler to let it know what we want it to
run within our app, namely, the AroundMe.Scheduler, we give it a name, a description
and so on.
In order for the main AroundMe app to register our AroundMe.Scheduler, we'll need to
add a reference to it:
1. In the AroundMe project, right-click the References folder
2. Choose Add Reference ...
This will pop up the Reference Manager dialog ...
1. Choose the Solutions | Project tab
2. Place a check-mark next to the AroundMe.Scheduler project
3. Click OK
Now we should be able to run our application in the Phone Emulator to see it at work.
5. Run the app in the Phone Emulator to make sure it works.
When running the app from a previous try, you may have left it on the lock screen to see
whether or not it worked. If you did leave the Emulator on the lock screen, you may see
this error message:
You merely need to unlock the Phone Emulator's lock screen by hitting F12 on the
keyboard and / or use the swipe motion to unlock the phone. Then you can re-run /
debug the app.
To test this, you just need to go through our usual script ... i.e., search, select a few
Flickr images, click the Set button, approve the dialog boxes that appear, then hit the
F12 key on your computer's keyboard twice to simulate clicking the phone's power
button twice to lock then awaken the phone. Undoubtedly your photos will look different
from mine.
After 30 seconds (maybe a little more, maybe a little less) it should change images to
another of your selections.
And so we've completed the development of our project successfully!
But wait, there's just one or two more things we need to do before we submit this for
inclusion in the store.
6. Clean up the code removing development only code, adding other details before
submitting to the Store
There are a few final steps that we would need to take before we're ready to ship this
and submit it to the Store for inclusion.
First, you'll recall in this lesson we used the LaunchForTest() method a couple of times
to test the scheduled background task agent. Remember this?
We'll need to search the entire solution for the LaunchForTest method ... recall we have
a reference to it as depicted (above) in the AroundMe.Scheduler project's
ScheduledAgent.cs file as well as in the AroundMe project's App.xaml.cs file. As I said
earlier, you either want to comment these out OR wrap them in a check for the
debugger:
if (Debugger.IsAttached) {
}
Obviously we'll want to thoroughly test our app. I would recommend allowing friends and
family to use it, or better yet, find a group of developers in your area that would be
willing to take a look and make recommendations. I was at a Nokia developer event
recently and there were a lot of developers exchanging business cards and showing
each other what they were working on. This can be a great way to inexpensively find
testers, and to build some contacts in the development community. So, I would
recommend getting involved by checking in on their Events page:
Another option is to locate the Microsoft developer evangelist in your area and ask them
if they have any events scheduled. I would search for something like:
· Microsoft developer evangelist [Your city, state, region or country here]
· windows phone user group [Your city, state, region or country here]
NOTE: You may have to be creative and try different wording to find a group or
evangelist near you.
Developer evangelists usually have blogs, twitter accounts or mailing lists through
MSDN or TechNet that you can subscribe to in order to learn about meet-ups, user
groups or code camps in your local area. Or you could always write the developer
evangelist an email and introduce yourself.
Once you're ready to submit to the store, you should pay particular attention to the APIs
you'll be calling. For example, we used the Windows Phone 8 Maps control and Flickr.
Make sure the account you're using is up to date, and you're using the correct
credentials.
With regards to the Map control, we will need to get an ApplicationId and an
Authentication Token from the developer center. For a full explanation of this, check out
this page:
In a nut shell, I'll add a Loaded event handler:
And will implement the code like so ... I've comment it out for now:
If you recall from Lesson 23, we saw an optional set of tasks including Map Services ...
... this is where you would retrieve the token and the application id required to the use
map services in your app. You would merely replace those literal strings with the
supplied tokens from the store.
Recap
To recap, the big takeaway was how to create a scheduled task and share common
code in a library, and how to test the scheduled task in the Emulator. We also learned
about final steps, not the least of which is how to receive a token for Map Services.
In the next lesson, I'll have just a few comments and will wrap up this series.
No comments:
Post a Comment