Windows Phone 7 Pitfall: The Dispatcher and Deactivation

by Mister Goodcat 27. October 2011 15:33

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:

   1: public static class Data
   2: {
   3:     private static ObservableCollection<DataItem> _items = new ObservableCollection<DataItem>();
   4:     public static ObservableCollection<DataItem> Items
   5:     {
   6:         get { return _items; }
   7:     }
   8:  
   9:     public static void AddItem(DataItem item)
  10:     {
  11:         Deployment.Current.Dispatcher.BeginInvoke(() =>
  12:         {
  13:             Items.Add(item);
  14:         });
  15:     }
  16: }

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: , , ,

Programming

Comments (2) -

11/17/2011 7:08:24 AM #

Mayank Mehta

hi ...i have a quick question for you

In my code i process camera image and call NavigationService GoBack...  inside Dispatcher - Begininvoke

1.
but before that Nav Goback finishes processing i click power button ....i navigate back to the app after few seconds......i dont see navigation happened... was wondering what happens to Dispatcher Begininvoke when application is pushed to dormant ?

2.  Now after hit power button first time .....i hit it again very quickly...and navigate to the app ...it fires NavigationTO event ....which also has a GoBack in it....since first Goback was not executed ...the second was causes error saying - "Navigation in progress"

Can you please advice how should i handle this scenario ....i m ean how can  i let first goback finish? should i add manual delay after activated?

Secondly, what happen is code in beginInvoke method that is fired before NavigationFrom and Deactivate event ? are they suspended for ever ....or they started again when code is activated? or does it executes but wont update the UI since it has been pushed to dormant stage ?

Mayank Mehta Vereinigte Staaten |

11/18/2011 8:17:39 AM #

Mister Goodcat

Hi. About your first and last question: I haven't looked at that in detail, but I believe that if the code is invoked before the deactivation, it is executed. But of course in the case of navigation, if the navigation has not completed before deactivation (using a hardware button), then the page that is navigated to is not added to the navigation stack, so it looks as if the navigation never happened when you return. This sounds very much as expected and by design.

Regarding the "navigation in progress" error, I don't believe that in your case it is due to the previous navigation has not finished, but because the "navigation to" is not done yet. Try using the dispatcher to invoke the go back after the "navigation to" has completely finished, that should solve the problem.

Mister Goodcat Germany |

Comments are closed