@Tyler, Cyberherbalist: I regret I cannot just 'add a comment' to your discussion right there. I'm yet too low on reputation or whatever. I dont have any blog to post it there and give you a link to it, either. I'll just write here, because I think it is worth noticing and writing somewhere, so others may also check/use/permormanceprofit from that.
..so, excuse me for this "offtopic article" here :)
While I don't know how to solve the problem nicely - my solution usually is to collect the navigation history stack by hand, put it into iso, and restore on activation and jump to the right/last place - I can tell you a little bit about the tombstoning.
The matter is, that if your application is deactivated, that does not mean that it is tombstoned. Deactivation simply means that your aplication is swapped out of the screen. In the Mango release, you can tap&hold the "Back" device button and see what applications are opened currently, and jump to any. "Activated" is when your application is woken up. Even in the very early releases of the WP7 SDK+Emulator, I was calling external mediaplayer from my applications, and my applications almost never were physically killed. Deactivated/Activated always resumed my application just right there where it was 'halted'. All in-memory objects were untouched.
Tombstoning occurs when the device is low on resources, and must "kill" some background tasks to free up the memory. I believe it may also occur when the device's screensaver dropsdown, and the device is in standby for a prolonged time. Tombstoning will literally kill your application, all in-memory objects will be destroyed/removed and so on. The only thing that will survive is the AppSettings and the ISO store. Tombstoning is guaranteed to happen ONLY if your application is in deactivated state.
So, what life-cycles you can see?
1) "Freeze/Standby/Unfocus":
- Launching
- ... (working)
- Deactivated (moved to background)
- ... (kept in memory, maybe process/thread is frozen, but I doubt)
- (...)
- Activated (moved to foreground, no navigation occurs)
- ... (working)
- Closing
2) "Tombstoned":
- Launching
- ... (working)
- Deactivated (moved to background)
- ... (kept in memory, maybe process/thread is frozen, but I doubt)
- ... (tombstoned, removed from memory, everything destroyed)
- (...)
- ... (clean application object constructed)
- Activated (moved to foreground)
- Navigated (I think, always to the first, default page set in the manifest, but I'm not sure now)
- (working)
- Closing
This means, that tombstoning may be a little hard to detect, but also means, that you always have a time to try to same your state in the deactivated-eventhandler.
This also means, that (unless there is some notification service that I do not know of yet), the only way to detect if a resume-from-tombstone is to rely on ... is to rely on the most deadly/unpleasant effect of it: on having your memory purged.
Imagine a simpliest case: your App object has a property "private bool _tomb_test". It may be ANY property/field of ANY type. You can use your "object ViewModel{get;set}" as well for that.
First thing to remember is, NOT to set it in the constructor, and do NOT assign a inline default value. Just leave it floating. The compiler/runtime will set it to the default false whenever the App object is created freshly new. And that's the point!
now:
- in Launched (not ctor!!!), set "_tomb_test = true"
- in deactivated, save your minimum persistent state to ISO
- in activated, check:
- if _tomb_test==false that means, you were tombstoned. your memory is clean, all objects were destroyed. restore your state according to data last written to the ISO, and then start all jobs to refill/redownload/reload/recalculate/etc all the other parts of the app state
- but, if _tomb_test==true, that means that the tombstoning did not occur. your memory is untouched. maybe just the GC kicked in and collected the dead. all objects that were living are still alive. the app is free to just run as if nothing ever happened.
While it may look pretty, please excuse me for this lengthy disclaimer:
I use it successfully in current application with 100% success, but I cannot say that this is guaranteed platform's behaviour. I have yet to find time to dig for the facts on MSDN. All of the above cames from my observations on the 7.0 and 7.1 SDK releases.
The small problem of the above approach is that it relies on the (unproven?) assumption, that the memory purge works in an all-or-nothing fashion.
That is, assumes that either all objects are purged, or no purge occurs (current fact: the App object and is observed to be re-created from scratch, and even an initial navigation occurs to recreate the UI. In other cases neither of it happens).
For the assumption to be wrong... I just cannot imagine who and why would ever decide to implement partial memory purge in the .net world. Full purge means killing the app. Partial purge means destroying random living objects in the GC generations and leaving all the rest alive with dangling handles, or with nulled handles. I cannot imagine it. I have not observed it. Thus, I assume it's all-or-nothing.
BTW. If you ever observe a partial purge and find a mostly-deterministic way to cause it, so others may see it too, please broadcast it loudly! :)