I have a UWP app that lets users create and modify text documents. I'm having a hard time getting the save mechanism to work with the app suspend/resume lifecycle.
Here is what I have:
Dispatcher.RunAsync()
When the app is being suspended:
ExtendedExecutionSession
My problem:
Dispatcher.RunAsync()
. The background queue waits for this task to finish, but it never does, because by that time, the UI thread has been stopped already.→ So my final save operation is never executed, because the background queue is stuck waiting to update the UI.
Here's a flow diagram:
A few questions that arise:
Dispatcher.RunAsync()
, will this at least finish or "freeze"?To summarize the problem:
While the app is being suspended, I must ensure that I wait for a potentially pending disk access on a background thread to finish before I finally save the document one more time in case my app gets terminated later on.
This sounds all too familiar!
What I understood from your question is the save operation ends with updating the UI, but since it's running in a background thread, it cannot touch the UI unless you schedule it with the Dispatcher
. Just to visualize, does your code look something like this:
public async Task SaveAsync()
{
// .. save to disk ..
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// update UI here
}
// Save Complete!
}
If so, the problem is that a UI update is part of a queued background operation and if anything is blocked by either the UI update or the Dispatcher
itself, no other operation will begin until you reach the line //Save Complete!
, which never happens if the Dispatcher
never executes your action.
Instead of concerning the save operation with UI updates, try exposing events that will trigger UI updates. For example:
public event EventHandler SaveStarted;
public event EventHandler SaveCompleted;
public async Task SaveAsync()
{
SaveStarted?.Invoke(this, EventArgs.Empty);
// .. save to disk ..
SaveCompleted?.Invoke(this, EventArgs.Empty);
}
Your view can attach handlers to these events and show/hide the appropriate visuals, while your suspend handler can can safely call the SaveAsync()
method without worrying about the UI.
When the view is Unloaded
, make sure you detach these event handlers. Your save operation will not block the queue when raising these events.
Update 1
Yes, your event handlers must run on the UI thread using the Dispatcher
:
public void OnSaveCompleted(object sender, EventArgs e)
{
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// update UI here...
}
}
Because the handler is forced to return void
, it will not block the background thread.
Update 2
Sounds like you have some tight coupling. You'll need to redesign your application such that your text-document logic sits in a separate project. I always code in a .Net Standard project which forces all UI out. The project will not allow any UI code whatsoever and thus keeping your logic clean and decoupled.
If all your code sits inside UWP projects, then it's very easy to start including view logic, XAML controls, and in your case, the Dispatcher
in parts of your app that have nothing to do with presentation.
In your case, you'll need figure out how to make your text-document logic become the owner of the state of the document, not the UI. hasChanges
should only be modified by the same logic that owns SaveAsync()
. If something breaks/blocks/fails/etc during a UI operation and it never makes it successfully to the text-document, then it's an incomplete operation and should not be part of the document, nor should it be the responsibility of the document to worry about it when it's time to suspend & save to disk.
If UWP kills your app for any reason during one of these operations, then it's gone. Your next task is to figure out how to resume this operation when the application wakes-up again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With