简体   繁体   中英

WP7 recover from Tombstone and return to page

Is there an nice / elegant way to get back to the page a user was on when recovering from a tombstone? I'm not sure if my app or its just the way things work, but I always end up back on my Main page.

My app is setup with a main page that has a Pivot Control and several of the Pivot Items will navigate to new pages. My Naigation looks something like this if it makes sense:

PivotItem1 -> PageA
PivotItem2 -> PageB -> PageC
PivotItem3 -> PageD -> PageE - PageF (Pressing Back Button on PageF will use NonLinear Navigation Service back to Main page)

So if the user tombstones on any of the pages I want to return them to that page and have the BackStack available so the navigation doesn't get screwed up.

I'm using MVVM Light and NonLinear Navigation Service if that helps or hurts what I'm trying to accomplish.

@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: :)

I have a similarly navigation-heavy app out there and it does what you want (also uses NonLinear Navigation Service). In my case I store the user's current page/pivot item in isolated storage, along with a token (or bool) indicating whether or not the app is returning from a tombstoning. When the user navigates to a page, the page load sets the current page value in the iso, and if the page has a pivot, it also sets the current pivot to the first one. The pivot_changed event has code to change the new current pivot to one the user just changed to.

When the app is in the process of tombstoning, the app_deactivate event fires, and it is here that I set the IsTombstoned value in iso to true.

When the user returns from tombstoning, the main page_load first checks to see if the app is reactivating from being tombstoned (IsTombstoned=true), and if so, it navigates immediately to the page whose name was stored in iso. When the now-current page_load fires it checks to see if IsTombstoned=true, and if so, sets it to false (back to normal), and if there's a pivot on the page, sets the selected pivot item to the stored current pivot. If the page doesn't have a pivot control, the current pivot is left blank. If there was dynamic content in the page when it was tombstoned, you'll also need to provide for its restoration.

Sorry I'm not showing you the code, but am away from workstation and I can't get to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM