Announcing: Phone as a Remote Control Fixing NLog's Logging to a Web Service

Windows Phone 7 Pitfall: The Dispatcher and Deactivation

Published on Thursday, October 27, 2011 1:33:43 PM UTC in Programming

When you're developing an application or component that does some sort of background processing, you are often in need to dispatch messages back to the UI thread, for example to report progress to the user, to otherwise manipulate UI elements, or to execute any logic that is bound to the UI thread by platform limitations or similar circumstances. On Windows Phone, a simple way to do this is to use the globally accessible Deployment.Current.Dispatcher object that allows you to post messages to the UI thread. More flexible methods capture the current (dispatcher) synchronization context, if applicable, and use that to post messages, for example. No matter what method is used, there's a subtle problem with that which can come quite unexpected. I don't dare to call this a bug (yet); I think it is simply a side effect and logical consequence of how the application lifetime is handled on the phone, however if you're not prepared for it, it can cause a lot of confusion.

Scenario

To demonstrate the issue, I have created a very simple application. It has a globally accessible data container with an ObservableCollection to easily display the content in the UI. The data container also has a method to add new items to that collection, and this method uses Dispatcher.BeginInvoke internally. Like this:

public static class Data
{
    private static ObservableCollection<DataItem> _items = new ObservableCollection<DataItem>();
    public static ObservableCollection<DataItem> Items
    {
        get { return _items; }
    }

    public static void AddItem(DataItem item)
    {
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            Items.Add(item);
        });
    }
}

This global data container is then used in various places of the application to add new items, in particular in the "Activated" and "Deactivated" event handler of the phone application service, and in the "OnNavigatedTo" and "OnNavigatedFrom" overrides of the main page. All items are shown in a list box on the main page. In addition I have added debug output statements in all these places to immediately print what happens.

Tombstoning

First let's activate Tombstoning in the project properties to suppress the new fast app switching feature and see what happens. When you navigate away from the application and return to it, none of the data is preserved (since we didn't add any logic for that). What happens is that two entries are visible in the list box: one originating in the "Activated" event handler, and one from the "OnNavigatedTo" override in the main page:

image

A look at the debug output confirms that everything went as expected without surprises:

Activated event handler.

Add Item: about to invoke dispatcher for item 'Activated event handler.'

Add Item: actually adding item 'Activated event handler.'

OnNavigatedTo override.

Add Item: about to invoke dispatcher for item 'Navigated to main page...'

Add Item: actually adding item 'Navigated to main page...'

A first interesting detail however is that when you scroll up you will realize that the debug output does not contain any entries for actually adding the items to the collection during deactivation. It looks like:

OnNavigatedFrom override.

Add Item: about to invoke dispatcher for item 'Navigating away from main page...'

Deactivated event handler.

Add Item: about to invoke dispatcher for item 'Deactivated event handler.'

This means that the "BeginInvoke" method of the dispatcher was executed, but the actual operation that was queued never was. Because the application state is not preserved anyway, this behavior did not become apparent or even cause harm though in this case.

Fast Application Switching

Once we disable Tombstoning upon deactivation in the project properties, the application will make use of the new fast app switching feature in Mango once we navigate away and return to it later. Let's take a look at the list box first:

image

At first glance, this looks as expected. Since the application state has been preserved, we indeed expect to see the two deactivation messages too. The problem however becomes apparent when you take a look at the debug output again, to see when those actions were actually performed.

OnNavigatedFrom override.

Add Item: about to invoke dispatcher for item 'Navigating away from main page...'

Deactivated event handler.

Add Item: about to invoke dispatcher for item 'Deactivated event handler.'

In the first block you can then see that the behavior is identical to normal Tombstoning at first, which means when the user navigates away from the application, the two deactivation messages are NOT added to the data container.

Activated event handler.

Add Item: about to invoke dispatcher for item 'Activated event handler.'

OnNavigatedTo override.

Add Item: about to invoke dispatcher for item 'Navigated to main page...'

Add Item: actually adding item 'Navigating away from main page...'

Add Item: actually adding item 'Deactivated event handler.'

Add Item: actually adding item 'Activated event handler.'

Add Item: actually adding item 'Navigated to main page...'

The following block reveals what happens on activation then: first the "Activated" event handler of the phone application service is executed, then the "OnNavigatedTo" override of the page is executed, and only after that are the items that have been queued on deactivation(!) added to the collection! Wow, now that is unexpected.

What does this mean? The result of this behavior is that the above "Navigating away" and "Deactivated" items will be processed when the application actually is already *activated again, *and the user has finished navigating back to the page in question. The following diagram shows the behavior once again:

image

Since the sample application is a bit pointless, let me explain a real-world example that shows what problems may arise from this: say you have a phone application page that does some background processing. When the processing has finished or is otherwise stopped, a notification is posted to the UI thread so the page can navigate back to the previous page automatically. The background processing is also stopped when the application is deactivated. As a convenience for the user, when they return to the application, the background processing is resumed automatically when it was in progress at deactivation time.

Now for classic tombstoning this works, because even though when the application is deactivated notification messages are posted to the UI thread, they are never actually consumed; no back navigation is performed. With fast app switching however, the following happens: when the application is activated, the processing is correctly resumed at first (in the "Activated" event handler or the "OnNavigatedTo" override). After(!) that however the previous notification that the background processing was stopped (dispatched during deactivation!) is consumed on the UI thread, causing a back navigation. The result is that the user experience is ruined (users are on the wrong page now) and the application state is inconsistent (because the background processing is actually still running).

Expectation

It was hard for me to find a definite answer to what my expectation actually is with this constellation. What is a correct behavior? The more I thought about it, the more I came to the conclusion that both behaviors are wrong. What I would expect is actually something like this:

image

The platform already has a built-in limit of 10 seconds for applications to perform and finish any tasks that are executed in application lifetime event handlers (this is explicitly mentioned on MSDN here, for example). After the 10 seconds limit, the application is terminated if it hasn't finished processing. It would be nice if that limit and processing time also included actions I've asynchronously posted using the dispatcher, which would solve the above mentioned problems in an elegant way.

Solutions

So what are solutions to solve this with the current implementation? We don't have public access to information about the queued messages in the dispatcher, and of course there's also no way to manually manipulate them. So obviously there are two possible attempts: a) prevent posting to the UI thread once the application gets deactivated, or b) ignore undesired messages posted to the UI thread after activation. Both have a hacky smell to them, but the first one seems more promising. However, I've not yet thought about how this could be handled consistently or if it is possible at all; if I manage to come up with some clean solution, I will do a follow-up post about it.

Feel free to comment below or contact me if you have additional thoughts or other solutions.

Tags: Dispatcher · Fast App Switching · Mango · Windows Phone 7