(X) Hide this
    • Login
    • Join
      • Say No Bots Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows Phone 7 - Part #4: The application lifecycle

(3 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
0 comments   /   posted on Feb 08, 2011
Categories:   Windows Phone

This article is compatible with the latest version of Silverlight for Windows Phone 7.

This is part 4 from the article series on Windows Phone 7.

In the previous three numbers of the series we have gone through a series of arguments that finally have let us to start building applications for the phone. We have spoken about the most basics arguments about the creation of a project and the initial phases of the program, then we started to enlarge our scope and we focused on the page, then we have open again the scope and we spoken about the problem of having multiple pages. Now, it is time to consider the application we write as part of a more wide system and watching at what it happen when our software runs on the phone and is become one of the many apps used by the final user.

A missing feature

When the team started to design the architecture of your Windows Phone 7 they decided to give not support to the multi-threading. Pay attention, this does not means your phone is not multi-threaded, there is a number of aspects that let you understand that, under the hoods, the runtime of the phone is multi-threaded for sure. When you are writing an email and you receive a call, or an alarm sounds, it is pretty clear that there is a level on your operating system that runs multiple threads simultaneously. Since the core of the system runs as a normal operating system with multiple threads, you have to remember that Silverlight is situated in a very restricted room on top of the tower that separate us from the very far basis of the operating system.

The choice of the designers was to not give to Silverlight the ability of run in background. Your software only runs when the UI is visible to the user and he can interact with the pages. This does not mean you cannot start background threads. The base class library has an useful namespace, called System.Threading, that you can use for this purpose. Also you have to be aware that the rendering system of the runtime itself runs two separate threads called UI thread an composition thread that collaborate to give better experience to the user.

What we really miss is the capability of "register" an application like a service and having it running almost always. Please do not live this limitation as a real obstacle. If you consider that the most sold apps for other platforms are task managers you can understand why the designers decided to put this limit to developers. Background threads are very powerful but often they make the user less conscious about what his phone is doing. So it start to emerge doubts about the battery consumption, and the worst about the use of the connection he is paying. And this trigger the buy of a task manager that helps the user to understand what it is happening.

The application lifecycle

Given that "there can be only one" application running at a given time, you have to understand that, due to navigation about which we have spoken in the previous number,  there are many ways the user can enter and exit the software. Navigation is something that exists also across the applications and when the user navigates he can enter the previous application if he follows the backstack. Also, there can be a number of events that can impact the lifecycle of the program. An user can press the start button and return to the main menu, he can receive an incoming call or the application itself may starts a "Launcher" or a "Chooser". In each of these cases the runtime suddenly closes the running application and then it starts another one.

Just to make things a little bit more complicated, you have also to be aware that there is actions that suspend the software and other actions that simply close it definitively. Let me make an example. When you are in the middle of running a program and you push the start button, the runtime suspends the execution but it presumes you can return to the suspended application because it is pushed into the backstack. So it operates what is commonly named "tombstoning" that means it deactivates the application but it saves it state for a later reprise. Differently, and this may sounds strange, when you push the back button and you exit the program because the backstack takes you to the previous app the only way you have to return to the application you are leaving is running it again from the start menu. There is not any way you can move forward just because it does not exist in the backstack anymore. So the runtime takes a strong decision and definitely closes it. No tombstoning, it is simply erased without any possibility of reprise.

Handling the lifecycle at two levels

The sudden closure of the your software may trigger a series of issues, if you don't handle these situations correctly. To make the user experience almost similar to what you would have if there would be a real multi threading it is important to handle the a couple of lifecycle event at two levels, the application level and the page level.

The application level includes the state of the entire application including everything that is not directly related to the visual aspect of the page. As an example, if you have a service running in a background thread, this may be have application-wide state that you need to save before tombstoning. For this purpose the runtime exposes four events that are raised during the phases of the tombstoning, during the startup and closing of the software. Here is the events:

Launching: This event raises when the application is opened for the first time.

Closing: raised by the runtime when it definitively closes the application.

Activating: this event identifies when the application is resumed after the tombstoning.

Deactivating: when you got this event you know the application is about to be tombstoned.

Into the App.xaml.cs file you can find the four handlers already hooked up to the events. If you go deep into the code and the XAML markup of this file it becomes evident the source of the events is the PhoneApplicationService instance that is present inside the LifetimeObjects section. We already encountered this service in the previous article when we had to save the state of the page during the navigation.

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
 
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}
 
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}
 
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
}

In these events you can save the state using the PhoneApplicationService.Current.State dictionary being aware that this dictionary is retaind in memory when the application is tombstoned and not when it is definitively closed. The correct way of handling state is the following:

1) in the deactivating event you have to save, into the transient "State" dictionary, the informations you know it can be lost if the application does not restart again. If you have some sensitive data you want to save also if the user completely restarts the application the best is to use a persistent storage like the IsolatedStorage.

2) when in the activating event you have to read back the informations and recreate the exact state you have saved. It is a good rule of thumb, that at the reprise the user will find exactly what he leaved during tombstoning.

3) in the closing event you have to manage to save the required information to a persistent storage. The deactivating and closing events are mutually exclusive, so you have to write an entirely separate process for the state management.

4) during the Launching event you must restore only the data you have saved in the closing event. This event means that the user has restarted the application from scratch so he expects to find it in its initial state. This event is also mutually exclusive with the activating event.

So if you imagine to have an application that pull data from a wcf service periodically we can have some stored data in an "Items" collection that we want to save across restarts and a "Current" property that only need to be saved during tombstoning; here is a sample code for the App.xaml.cs.

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
    MyApplicationState.Instance.Items = this.LoadFromIsolatedStorage();
    MyApplicationState.Instance.Current = null;
}
 
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
    MyApplicationState.Instance.Items = this.LoadFromIsolatedStorage();
 
    if (PhoneApplicationService.Current.State.ContainsKey("CurrentItem"))
    {
        MyApplicationState.Instance.Current = PhoneApplicationService.Current.State["CurrentItem"] as ItemData;
        PhoneApplicationService.Current.State.Remove("CurrentItem");
    }
    else
        MyApplicationState.Instance.Current = null;
}
 
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    this.SaveToIsolatedStorage(MyApplicationState.Instance.Items);
    PhoneApplicationService.Current.State["CurrentItem"] = MyApplicationState.Instance.Current;
}
 
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
    this.SaveToIsolatedStorage(MyApplicationState.Instance.Items);
}
 
private List<ItemData> LoadFromIsolatedStorage()
{
    using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (file.FileExists("app.state"))
        {
            using (IsolatedStorageFileStream stream = file.OpenFile("app.state", System.IO.FileMode.Open))
                return this.DeserializeXml(stream);
        }
 
        return new List<ItemData>();
    }
}
 
private List<ItemData> DeserializeXml(IsolatedStorageFileStream stream)
{
    XmlSerializer serializer = new XmlSerializer(typeof(List<ItemData>));
    return serializer.Deserialize(stream) as List<ItemData>;
}
 
private void SaveToIsolatedStorage(List<ItemData> state)
{
    using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (IsolatedStorageFileStream stream = file.OpenFile("app.state", System.IO.FileMode.Create))
            this.SerializeXml(stream, state);
    }
}
 
private void SerializeXml(IsolatedStorageFileStream stream, List<ItemData> state)
{
    XmlSerializer serializer = new XmlSerializer(typeof(List<ItemData>));
    serializer.Serialize(stream, state);
}

Handling the state at the application level does not suffice. There are lot of details you should manage, during tombstoning, also at the page level. As an example you can imagine a form where the user have to input some data, the level of a game or the point where the user scrolled an image. All these detail do not refer directly to the application. They makes sense only when the tombstoning happens while the page is open. For these cases it is available a state bag, exposed by a property of the page. Unfortunately the activating and deactivating events cannot be handled at the page level. The problem comes from the fact that, during the reprise after tombstoning,  the activating event is raised long time before the page instance is recreated. So if you attach the activating event of the PhoneApplicationService, this is never raised because the code that effectively attache the event is executed after the event is raised.

To handle the tombstoning at the page level you have to use the navigation events to understand what is happening. Particularly the OnNavigatedTo event, that is raised as part of the tombstoning process uses an empty parameter in this case. Infact, the NavigationEventArgs.Content property, that usually indicated the target of the navigatione, is empty when the navigation is pointed to exit from the application scope. And this happen only during the tombstoning. Here is how to persist the state of a textbox this way:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    if (this.State.ContainsKey("Message"))
    {
        this.message.Text = this.State["Message"] as string;
        this.State.Remove("Message");
    }
 
    base.OnNavigatedTo(e);
}
 
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    if (e.Content == null) // TOMBSTONING
    {
        State.Add("Message", message.Text);
    }
 
    base.OnNavigatedFrom(e);
}

The purpose of this code is to persist the content of the message textbox and read it again after the reprise.

Conclusion

With this article it closes the first part of the series, the one I dedicated to the architecture and the basic informations that is required to know to develop for the Windows Phone 7. From the next number we will start to examine the most advanced arguments that are not immediately required but if you use them, they makes the difference between an application and a great application.

About the Author

Andrea BoschinAndrea Boschin is 41 years old from Italy and currently lives and works in Treviso, a beautiful town near Venice. He started to work in the IT relatively late after doing some various jobs like graphic designer and school teacher. Finally he started to work into the web and learned by himself to program in VB and ASP and later in C# and ASP.NET. Since the start of his work, Andrea found he likes to learn new technologies and take them into the real world. This happened with ASP.NET, the source of his first two MVP awards, and recently with Silverlight, that he started to use from the v1.0 in some real projects.
 


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series