In the first two Understanding the Windows Phone Application Execution Model, Tombstoning, Launcher and Choosers, and… Few More Things on the Way posts (part 1, and part 2) you learned about the different application lifecycle events – Launching, Deactivated, Closing, and Activated, and how they differ from one another. Based on that knowledge and this code, we’ll move forward.
Saving the transient state and navigating to the right page
As you will recall, the second page of our simple application includes two textboxes, one for allowing the user to enter a phone number that will be used to save new contact information, and a second textbox for allowing the user to enter an SMS message to be sent. This will become useful when we start playing with Launchers and Choosers in the context of tombstoning applications.
Let’s start by defining the problem. The easiest way is simply to run a small experiment:
From this little experiment you have learned that data entered into your application is not automatically saved when your application deactivates (remember, your application gets terminated). And since reactivating an application really means starting a new instance of the application with some tombstoned information, the page that you land on – in our case page 2, the DetailsPage, is a brand new instance of that page. This indicates that by default all the controls have no data, unless you load data into them.
The most important thing to remember about tombstoned applications is the simple fact that the user may NOT return to your application, and therefore your application may not be reactivated. If you anticipate this happening, you need to save to disk any data that you wish to restore at a later time. It is completely up to you and your responsibility as a developer to handle the saving and retrieving of data in your application
We identified two sets of data types that we want to store (copied from MSDN):
We will address the persistent data and working with isolated storage in a future post. For now, let’s focus on managing the transient data and using the State dictionary.
One of the new classes in the SDK is PhoneApplicationService. The PhoneApplicationService class provides access to various conditions of the application’s lifetime. This includes management of the application’s idle behavior and management of the application’s state when it becomes active or inactive. This class plays a major role in the tombstoning game, since it exposes the Launching, Deactivate, Activated, and Closing events that have the corresponding methods (which you already saw) in the App.xaml.cs file. This class also includes a read-only State property of type IDictionary<string, object>. The importance of this dictionary is that it is persisted on your application’s behalf by the Windows Phone operating system when your application gets tombstoned. When the application reactivates, the objects that placed in the dictionary are returned. You don’t need to save these transient objects to disk as long as you expect your application to return from the tombstoned state. Therefore, if you intend to use the State dictionary, make sure you save only transient data in it– information that you don’t mind losing and that is local to the current application instance. In our example, we’ll save the phone number and SMS text message.
Please note that the objects you store in this dictionary have to be serializable, or else you will get an exception during the deactivating event about the inability of the operating system to sterilize or desterilize your object.
MSDN best practices recommend that you store transient page data in the State dictionary during the OnNavigatedFrom event, and load data during the NavigatedTo event.
I’ve update the application to save the phone number and the SMS message in the Store dictionary, and load it each time the page get navigated to. If a “completely” new instance (indicated by the “Lunching” event) of the application is started, the State dictionary is empty.
1: protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
3: Util.Trace("***** in DetailsPage: OnNavigatedFrom ( " + DateTime.Now.Ticks + " *****)");
5: //try to locate the phone number from previous save and simply override it
6: if (true == PhoneApplicationService.Current.State.ContainsKey(PhoneNumberKey))
8: //clear prev value
11: PhoneApplicationService.Current.State.Add(PhoneNumberKey, this.PhoneNumberTxt.Text);
13: //try to locate the SMS Messagefrom previous save and simply override it
14: if (true == PhoneApplicationService.Current.State.ContainsKey(SmsMessageKey))
16: //clear prev value
19: PhoneApplicationService.Current.State.Add(SmsMessageKey, this.MessageTxt.Text);
22: // Step 2
23: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
25: Util.Trace("***** in DetailsPage: OnNavigatedFrom ( " + DateTime.Now.Ticks + " *****)");
27: //try to locate the phone number from previous run
28: if (true == PhoneApplicationService.Current.State.ContainsKey(PhoneNumberKey))
30: string s = PhoneApplicationService.Current.State[PhoneNumberKey] as string;
31: if (!String.IsNullOrEmpty(s))
33: Util.Trace("***** in DetailsPage: OnNavigatedTo: Found phone number ( " +
34: DateTime.Now.Ticks + " *****)");
36: this.PhoneNumberTxt.Text = s;
40: // Step 2
41: //try to locate the phone SMS MSG from previous run
42: if (true == PhoneApplicationService.Current.State.ContainsKey(SmsMessageKey))
44: Util.Trace("***** in DetailsPage: OnNavigatedTo: Found Sms Msg ( " + DateTime.Now.Ticks + " *****)");
46: string s = PhoneApplicationService.Current.State[SmsMessageKey] as string;
47: if (!String.IsNullOrEmpty(s))
49: this.MessageTxt.Text = s;
Note that with the following changes, the information in page 2 is stored between page navigation. That is when you press the Back button in page 2, the application navigates back to page 1. If you click the “Navigate to next page” from page 1, your application navigates to page 2. The OnNavigateTo (on page 2) event is raised, and with the above code the control will be loaded with data. This is important because if you take a closer look at the trace in the Output windows in Visual Studio you will find that the each time you navigate to page 2, its constructor is executed. This indicates that each time you press the Back button from page 2, that page is destroyed, and therefore each time you navigate back from page 1 to page 2, the page is recreated. To observe this behavior for yourself, simply navigate back and forth between the two pages, and note that the page 2 CTOR is called each time you navigate from page 1 to page 2. If you comment out the OnNavigatedFrom method in your application, and navigate between the two pages, you will see that the information on the second page is not saved.
Now tombstone the application to see for yourself that the phone number and SMS message are being stored, not just between page navigations, but also after leaving the application and returning to it. Simply navigate to page 2, enter a phone number and some text, and press the Windows button to tombstone the application. You should see the deactivated trace and then the application terminated. Press the Back button and don’t forget to press F5 in Visual Studio to restart the debugging session. Your application will return from its tombstoned state and you will see the launching event, and then the DetailsPage (page 2) constructor. Next you will see the OnNavigatedTo trace and, if everything works, you will see the "Found phone number" and "Found Sms Msg" lines in the trace (and in the emulator), as shown in the next image.
All this is nice, and I hope it explains the way things work with tombstoning Windows Phone applications. But, now it is time to step up our game and show some cool things in action.
First, I’ve added a helper Logger class that logs all the traces and displays them in the MainPage (first page) textbox. The goal of the Logger is to prove to you without a doubt that your application gets terminated and that the State dictionary really works. The logger will also allow you to run your application from the emulator, not in debug mode, and still get some information back through the traces shown in the log textbox.
The Logger class includes a very simple (and most probably not that thread-safe) implementation of a Singleton. The main reason this class is a singleton is to show that even a singleton class is removed from memory when your application is terminated, as well as the order of events and the loading of data between the different events. This class has a string member, which is the log, and a DateTime member that saves the creation time of the Logger object. With the Logger class you can add new lines to the log, and get the entire log. This is good for debugging, which is exactly what the Logger is for. Each time you use the Util Class to add a trace, you also add it to the Logger.
Usually a singleton implementation doesn’t have a public constructor. However, you are going to save this class in the State dictionary, which means that the Logger class must be serializable and therefore must have a public default (empty) constructor. Otherwise you will get an exception when trying to deactivate or activate your application.
To “view” the log, I’ve added the OnNavigatedTo method to the MainPage.xaml.cl and loaded text in the log to the logTextBox control on the main page. Therefore each time you navigate to the main page of the application you will see the log file printed. Doing this enables you to view the trace in your application without the need to debug your application in VS (and thanks to Jaime Rodriguez for the tip). Let’s try it.
The interesting segment of this trace is the information between the dashed lines, and the following code snippet for which the Application Activated method is responsible.
1: if (true == PhoneApplicationService.Current.State.ContainsKey(LoggerKey))
3: Logger logger = (PhoneApplicationService.Current.State[LoggerKey] as Logger);
4: long timeDef = Logger.Instance.CreationTime.Ticks - logger.CreationTime.Ticks;
5: Util.Trace("-------------------------------------\n--> Time difference between Loggers = "
6: + timeDef
7: + "\n" + logger.GetLog()
8: + "\n-------------------------------------");
In this code, you can see that we are looking for the Logger object in the State dictionary. The object was put into the State dictionary in the deactivated event. Assuming the logger object is found, we are creating a temporary object called logger (note this is not the actual logger object that we are using in the current application). Next we compare the creation time of the Logger from the State to the singleton Logger instance of the “new” application that we just activated. As you can see in the trace, there is a difference in time; the logger from the State dictionary that we saved when we last deactivated the application is older than the new logger. From the code you can see that we can’t really initiate the older logger; its creation time and log are restored and printed to the textbox. So everything between the dashed lines is actually the previous run of the application up to the deactivated event.
The first line after the dashed line is shows the constructor of the second page, as expected, and it shows the “new” application that got reactivated and returned to the second page, from which we had deactivated the application.
Now you have seen all four events in action: Launching, Deactivated, Activated, and Closing. I hope you understand that when your application is not running, it is terminated, and any data you didn’t saved is gone. Then when you are return to the application, with either an Activated event or a Launching event, you get a NEW instance of the application (our little singleton experience proved that).
The State dictionary can save transient data between Deactivated and Activated events and will turn out to be a very useful tool in the next post where we’ll talk about choosers and launchers.
One important thing I am missing though is the subtle difference between an application that is tombstoned and one that is still dormant.
When deactivated the application first becomes dormant and remains fully in memory.
In a dormant state you don't have to do anything when the application is activated again.
Restoring a state might actually show strange behavior if UIElements are not updated to point (bound) to the restored state.
Only restore the state when the application is indeed tombstoned. That is when the device has reclaimed the memory of the application and the application is no longer alive in memory.
Keep up the good postings!
This sentence is wrong..
Your application will return from its tombstoned state and you will see the "launching" event, and then the DetailsPage (page 2) constructor
Its not "launching event", its activated event...
These 3 posts you made so far on this subject are just damn good (just the right amount of details, diagrams, explanations and code samples). Kudos!
May I make a wish here...could you make something similar for the 3 new controls (Pivot, Panarama, and Bing Maps) when the RTM is out? That is the next on my list of "got to learn, and learn well, in WP7"
...but first we need the controls and RTM :)
Again, excellent posts, keep it up!
These articles are very clear and helpful. I am anxiously awaiting part 4. Keep up the good work.
Hi Yochay ... Can we save some effort here by binding our viewmodel directly to the State dictionary? That way we get UI persisted 'for free'!
Can you tell me, how about "Loaded" and "Unloaded" event ?
What different with "OnNavigatedFrom" and "NavigatedTo" event ?
Yochay, you mention in Part 2 (flowchart) to use PhoneApplicationService.State in deactivated event handler and PhoneApplicationPage.State in the OnNavigatedFrom event handler. But in the samples you are using PhoneApplicationService.State for both event handlers. Am I missing something here?
Looks like I'm going to have to up my geek factor in order to consider a logger stepping up our game and showing some cool things in action... However, it is a great illustration of what is happening to the application state. Indeed, I went for a coffee run between reading Part 2 and 3. Logging the timestamps during each of the lifecycle events crossed my mind :P
Thanks for another well devised tutorial. Keep em coming!
@vva old habits die hard... although not required, this is easier to read and understand....
Not exactly on the topic of the post, but maybe it could be just
var state = PhoneApplicationService.Current.State;
state[PhoneNumberKey] = this.PhoneNumberTxt.Text;
state[SmsMessageKey] = this.MessageTxt.Text;
in the OnNavigatedFrom method? Or at least dropping 'true == ' in the boolean checks may be a good move? For the love of god, this is Samples. Children also read this (i.e. beginner developers).
@barts2108 Nah, reviews are saying it is better than the iPhone keyboard.
Uhm, its first time I see the onscreen keyboard... it looks like it still need a stylus. What a pity