Tombstoning in Phone 7 (Mango version)
Dormant application and tombstoned application essentially relate to the execution model of Phone 7 Mango applications (referred to as mango applications henceforth). Dormant application is a concept introduced with mango. But even before we go there, let's take a step back and understand why these are necessary. The point that many developers today feel confused about this is mostly due to their coming over from programming for applications that run on desktop.
If I were to draw an analogy, I can take an example that I often share with my team members. In the era of punch card and shared time slots to work on computers, people were very particular on the quality of code they wrote. Any error/unwanted bug at run time would mean all the time lost and another long wait before they get their slot again. Today, however with multicore 3GB upwards of RAM machines, it takes hardly few seconds to build your code and let complier catch the errors. The developers then happily fix each of these errors and move to next step. If the code didn't run no harm is done. Another 10-15 min... Issue resolved and they are back to running their application. In some sense programming for a desktop application is similar in that you are dealing with almost infinite resources. Unlimited local storage... Memory can mostly be managed and who cares about power management (or battery life for laptops). Moving to Phone 7 is like going back to the punch card days. Limited memory, limited processing power, limited local storage and limited battery power. The applications hence need to be well behaved citizens to work in harmony with each and no single application should try and steal the resources.
Unlike desktop applications, mango applications are always in full screen mode. The mango development hence uses this simple philosophy that what is not in view is not being used. So the moment user navigates away from your application by either hitting hardware back button, by hitting hardware start key, by answering incoming call, by clicking on a toast notification or even by locking the screen, your application can potentially be terminated to save phone resources. In Phone 7.0 third party applications weren't allowed to run in background at all. So the moment your application is no longer on foreground, it was pretty much on the way to termination.
Phone however isn't eagerly trying to terminate applications. To start with, the application is made dormant. The application continues to live in memory and hence the data that the application was working with is still available. If the Phone OS feels memory pressure it will then start to terminate the applications using a least used first kind of logic. Interestingly there is no notification sent to the application at this time, which I find a bit awkward. The only event that is available is when the application is made dormant and is the application deactivating event. So as per this design, you are forced to save to persistent storage (isolated storage) all that data that you will need 'if' your application got tombstoned. Do note that a tombstoned application may never be reactivated. Phone 7 allows at a time only 5 applications to remain in tombstoned state, as mentioned here.
Before we go further, let's dig a bit deeper here, as this is the area where most issues happen in terms of understanding. While there are various ways in which the application lost foreground to something else (we discussed this earlier), there are actually only 2 ways an application can be invoked. First is via a tile on home screen or from the application list. These are counted as one as in both cases, a new instance of the application is created, irrespective of if a dormant or tombstoned application instance existed or not. Second is by using hardware back button to navigate back to the application from another application.
If your application became dormant and then later got reactivated because the user used the back button to reach back to this application, then the instance is reloaded as is and as a developer you really don't need to do anything. The application along with its navigation stack is reloaded and it will work just as if you had never moved out of this application. A word of caution is that since the same instance is reloaded, things like constructor and Loaded event don't get called and hence the data will remain as is. An easy way to figure this out is by checking the value of e.IsApplicationInstancePreserved in Application_Activated event handler. If the value is true, it means that the application was only dormant and is now back to its regular execution. You can hence decide to not do anything at this time.
Assume you were on a page where user entered some data in a text box and the application became dormant for some reason. When the application is reactivated, the user is automatically taken to the very same page and the text box will retain what the user would have typed. If you handled NavigatedTo event in your code, then the e.NavigationMode property will have a value of NavigationMode.Back, indicating that this page was already loaded earlier and the user is returning back to the page. Combine this with IsApplicationInstancePreserved from Application_Activated event handler and you can figure out that you are actually returning from a dormant state.
Let's now look at if the application had been tombstoned. With the Mango SDK this is fairly easy to simulate as the project properties offer a flag Tombstone upon deactivation while debugging. Unlike dormant state, when the application is tombstoned, it is terminated and hence there is no longer a running instance and hence no longer any data in memory. At this time if a user gets back to the application using the hardware back button, a new instance of the application is created, however it is initialized using the data when the application got tombstoned. There is a bit of a caveat here, which we will look at shortly.
Phone provides PhoneApplicationService.State bag to store global transient state information and PhoneApplicationPage.State bag to store page level information that would be useful to have when the application is reactivated from tombstoned state. The state bag is restored and hence whatever values would have been stored in state bag can be accessed and used as appropriate. Note that the value of e.IsApplicationInstancePreserved in Application_Activated event handler is false, indicating the application had been tombstoned. Do understand that Launching and Activated events are mutually exclusive events. Only one of these is raised for an application at a time.
Let's relook to the earlier scenario where user typed some data in a text box. If the application got tombstoned, the data the user typed would be lost. To give user a seamless experience, you may want to show the same value when the user returns the application using back button. In this store this data in the Page's state bag. A good place to do would be in page's NavigatedFrom event handler. For restoring you can do that in page's NavigatedTo event handler. While restoring the state bag value, you may want to additionally verify the direction of navigation (e.NavigationMode) and if this returning from being tombstoned (e.IsApplicationInstancePreserved) to help decide if restoration is required or not. If NavigationMode is NavigationMode.New, then there is no need to try and restore and it indicates a new page instance.
As stated earlier, there is no additional indication/opportunity the application has to store any kind of data as the application moves from dormant to tombstoned state. So the decision to store any transient data that will be needed has to be taken assuming that application may potentially hit tombstoned state. On the flip side is the fact that only 5 applications can be tombstoned at a time and a user may never return to the tombstoned application, also means that you need to be very careful on picking what to store. Storing too much information will only bloat up storage space but never be used.
Existing an Application
Finally, this discussion will not be over without covering a regular existing/closing of the application. As already discussed this happens only if the user is on the main page of the application and then presses hardware back button to navigate away from the application to the previously navigated screen. In this case the application does not receive deactivated event but instead gets the closing event. At this time, the application may either just quit as is, or may decide to store some information to persistent store for use with the next run (of new instance). One simple example would be say highest score of a game. This is typically preserved across application instances and should be available on the next launch of the application. This is where isolated storage comes in. Data stored in isolated storage can be retrieved in Application_Launching event handler and used as appropriate.
Do however note that you cannot just rely on Application_Closing event handler to store such data to isolated storage, as the application may become dormant in between and may even be tombstoned. For example, while playing a game, you may get a call in between and your application becomes dormant. The user doesn't returns to the game right away, but may do that in another few days as well. Most likely the application will get tombstoned in between and be even pushed off the tombstone list. Hence the data that needs persistence will need to be stored to isolated storage in Application_Deactivated event handler as well.
With that I come to an end for this blog. Hope that after reading this, you would have a clear idea of different states of the application and how to use the different events to manage application storage needs. This post didn't show code samples as I was keen on getting the theory across. Feel free to comment back and share your views.