Расширенное выполнение не работает должным образом?
Я не могу получить ExtendedExecution
для правильной работы. Проблема в том, что событие Revoked
не запускается до завершения выполнения. Если взять образец:
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
Debug.WriteLine("Suspending in app");
var deferral = e.SuspendingOperation.GetDeferral();
using (var session = new ExtendedExecutionSession())
{
session.Reason = ExtendedExecutionReason.SavingData;
session.Description = "Upload Data";
session.Revoked += (s, a) => { Debug.WriteLine($"Extended execution revoked because of {a.Reason}"); };
var result = await session.RequestExtensionAsync();
if (result == ExtendedExecutionResult.Denied) Debug.WriteLine("Extended execution failed");
else
{
Debug.WriteLine("Executing");
await Task.Run(() => { Task.Delay(9000).Wait(); Debug.WriteLine("Finished the task"); });
Debug.WriteLine("Executing finished");
}
Debug.WriteLine("Suspending after execution");
}
deferral.Complete();
}
В документации указано, что при возобновлении приложения должно быть запущено событие Revoked
, но если вы попробуете код с прикрепленным отладчиком, то вы увидите, что вывод отладки выглядит нормально, но для этого вам нужно подождать 9000 мс показывать. Это означает, что код приостанавливается до окончания сеанса.
Самая большая проблема заключается в том, что если вы запускаете это без отладчика, запустите приложение, приостановите его, а затем возобновите, вы увидите черный экран в течение нескольких секунд, а затем ОС прекратит ваше приложение.
Я что-то упустил? Кто-нибудь заработал правильно?
Ответы
Ответ 1
Использование await
и Task
приводит к тому, что ваша оставшаяся задача остается в основном потоке, заставляя вас ждать с черным экраном.
Помните, что поведение await
заключается в том, чтобы запланировать выполнение в Dispatcher
, НЕ для начала нового потока, NOR планирует его выполнение в ThreadPool. Как следствие, больше сообщений UI не может быть обработано до конца Delay()
.
Просто выполняйте свою трудоемкую операцию в новом потоке, но убедитесь, что сеанс открыт до его завершения.
Взгляните на это https://msdn.microsoft.com/en-us/magazine/jj991977.aspx, чтобы получить хорошее представление о том, как выполняется выполнение
Ответ 2
Нет проблемы с пользовательским интерфейсом или что-то еще. Ваш код работает. Ваше ожидание неверно.
Используя ExtendedExecutionSession
, который вы сообщаете своему приложению, вам нужно время для сохранения, и оно не должно быть отменено до тех пор, пока вы не закончите. В вашем случае это займет около 9 секунд.
Попробуйте приостановить приложение, подождите 10 секунд и отмените его. Это произойдет немедленно.
Затем попробуйте приостановить приложение и отменить его до завершения сеанса. Теперь ExtendedExecutionSession
сообщит вашей ОС, что ваше приложение не может быть отменено, и ему нужно подождать, пока процесс сохранения не будет завершен. Это то, что вы хотите.
См. Microsoft doc для расширенного выполнения:
Запрос расширенного сеанса ExtendedExecutionReason.SavingData
, когда приложение находится в состоянии приостановки, создает потенциальную проблему, о которой вы должны знать. Если в состоянии приостановки запрашивается расширенный сеанс выполнения, и пользователь просит запустить приложение снова, может показаться, что он занимает много времени для запуска. Это связано с тем, что период времени расширенного сеанса выполнения должен завершиться до того, как старый экземпляр приложения может быть закрыт, и можно запустить новый экземпляр приложения. Время запуска программы запускается в жертву, чтобы гарантировать, что пользовательское состояние не потеряно.
То, что упоминается в разделе "Отзыв", тоже интересно для вас:
Когда событие Revoked
запускается для расширенного сеанса ExtendedExecutionReason.SavingData
, приложение имеет одну секунду, чтобы завершить выполняемую операцию и завершить приостановку.
Одной секунды недостаточно, чтобы завершить 9 секунд ожидания.
Чтобы исключить возможность отсроченного отображения вашего отладочного вывода, вы можете проверить его с добавлением текущего времени на выход.
У ОС, вероятно, проблема с закрытием сессии, потому что 9 секунд не заканчиваются.
Также обратите внимание на примечание EnterBackground:
Раньше ваш приостановленный обратный вызов был лучшим местом для сохранения состояния после того, как пользователь закончил сеанс с вашим приложением. Однако теперь приложение может продолжать работать в фоновом режиме, а затем вернуться на передний план из-за действия триггера, не достигнув приостановленного состояния. Лучшее место для сохранения данных после сеанса пользователя в вашем введенном обработчике событий.
Вероятно, вы захотите сделать свой код в случае, если событие Exiting
запущено.
Для OnSuspending
попробуйте выполнить ожидание с циклом for, который прерывает (отменяет процесс сохранения), как только происходит отмена, только в ожидании половины секунды за раз.
UPDATE:
... Или используйте Фоновая задача, поскольку приостановление, кажется, единственное надежное предупреждение перед завершением:
//
// Declare that your background task 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
Для "правильного" способа, как это должно быть сделано, см. официальный :
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;
});
}