There is no UI problem or anything. Your code works. Your expectation is wrong.
Using the ExtendedExecutionSession
you're telling your app you need time to save and it shall not be revoked until you are finished. In your case that takes about 9 seconds.
Try suspending the app, wait for 10 seconds an then revoke it. It will happen immediately.
Then try suspending the app and revoke it before the session is finished. Now the ExtendedExecutionSession
will tell your OS that your app cannot be revoked yet and it has to wait until the saving process is finished. That's what you want.
See Microsoft doc on extended execution:
Requesting a ExtendedExecutionReason.SavingData
extended execution session while the app is in the Suspending state creates a potential issue that you should be aware of. If an extended execution session is requested while in the Suspending state, and the user requests the app be launched again, it may appear to take a long time to launch. This is because the extended execution session time period must complete before the old instance of the app can be closed and a new instance of the app can be launched. Launch performance time is sacrificed in order to guarantee that user state is not lost.
What's mentioned in the section about "Revoke" is interesting for you, too:
When the Revoked
event is fired for an ExtendedExecutionReason.SavingData
extended execution session, the app has one second to complete the operation it was performing and finish Suspending.
One second is not enough to finish your 9 seconds waiting.
To eliminate the possibility of delayed display of your debug output, you can test it with adding the current time to the output.
The OS probably has a problem with the session not closing properly because the 9 seconds don't finish.
Also note the remark on EnterBackground:
Previously your suspending callback was the best place to save state after a user finished a session with your app. However, now an application may continue running in the background and then move back to the foreground due to trigger activity without ever reaching the suspended state. The best place to save data after a user session is in your entered background event handler.
You will probably want to do your code in case the Exiting
event is fired.
For OnSuspending
try doing the waiting with a for loop that breaks (cancelling the saving process) as soon as the revoke happens, only waiting for half a second at a time.
UPDATE:
...Or use a Background Task, as suspending seems to be the only reliable warning before termination:
//
// Declare that your background task's Run method makes asynchronous calls by
// using the async keyword.
//
public async void Run(IBackgroundTaskInstance taskInstance)
{
//
// Create the deferral by requesting it from the task instance.
//
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
//
// Call asynchronous method(s) using the await keyword.
//
var result = await ExampleMethodAsync();
//
// Once the asynchronous method(s) are done, close the deferral.
//
deferral.Complete();
}
UPDATE2:
For the "proper" way how it should be done, see the official example:
private async void OnSuspending(object sender, SuspendingEventArgs args)
{
suspendDeferral = args.SuspendingOperation.GetDeferral();
rootPage.NotifyUser("", NotifyType.StatusMessage);
using (var session = new ExtendedExecutionSession())
{
session.Reason = ExtendedExecutionReason.SavingData;
session.Description = "Pretending to save data to slow storage.";
session.Revoked += ExtendedExecutionSessionRevoked;
ExtendedExecutionResult result = await session.RequestExtensionAsync();
switch (result)
{
case ExtendedExecutionResult.Allowed:
// We can perform a longer save operation (e.g., upload to the cloud).
try
{
MainPage.DisplayToast("Performing a long save operation.");
cancellationTokenSource = new CancellationTokenSource();
await Task.Delay(TimeSpan.FromSeconds(10), cancellationTokenSource.Token);
MainPage.DisplayToast("Still saving.");
await Task.Delay(TimeSpan.FromSeconds(10), cancellationTokenSource.Token);
MainPage.DisplayToast("Long save complete.");
}
catch (TaskCanceledException) { }
break;
default:
case ExtendedExecutionResult.Denied:
// We must perform a fast save operation.
MainPage.DisplayToast("Performing a fast save operation.");
await Task.Delay(TimeSpan.FromSeconds(1));
MainPage.DisplayToast("Fast save complete.");
break;
}
session.Revoked -= ExtendedExecutionSessionRevoked;
}
suspendDeferral?.Complete();
suspendDeferral = null;
}
private async void ExtendedExecutionSessionRevoked(object sender, ExtendedExecutionRevokedEventArgs args)
{
//If session is revoked, make the OnSuspending event handler stop or the application will be terminated
if (cancellationTokenSource != null){ cancellationTokenSource.Cancel(); }
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
switch (args.Reason)
{
case ExtendedExecutionRevokedReason.Resumed:
// A resumed app has returned to the foreground
rootPage.NotifyUser("Extended execution revoked due to returning to foreground.", NotifyType.StatusMessage);
break;
case ExtendedExecutionRevokedReason.SystemPolicy:
//An app can be in the foreground or background when a revocation due to system policy occurs
MainPage.DisplayToast("Extended execution revoked due to system policy.");
rootPage.NotifyUser("Extended execution revoked due to system policy.", NotifyType.StatusMessage);
break;
}
suspendDeferral?.Complete();
suspendDeferral = null;
});
}