Расширенное выполнение не работает должным образом?
Я не могу получить 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 мс, чтобы они появились. Это означает, что код приостанавливается до завершения сеанса.
Самая большая проблема заключается в том, что если вы запустите это без подключенного отладчика, запустите приложение, приостановите и затем возобновите работу, вы увидите черный экран на несколько секунд, а затем ОС закроет ваше приложение.
Я что-то пропустил? Кто-нибудь работает правильно?
2 ответа
Использование await
а также Task
является причиной того, что ваша продолженная задача остается в главном потоке, заставляя вас ждать с черным экраном. Помните, что await
поведение заключается в планировании выполнения в Dispatcher
, НЕ для начала нового потока, NOR запланировать его выполнение в ThreadPool. Как следствие, больше сообщений UI не может быть обработано, пока Delay()
заканчивается.
Просто выполните трудоемкую операцию в новом потоке, но не закрывайте сеанс до его завершения.
Посмотрите этот https://msdn.microsoft.com/en-us/magazine/jj991977.aspx чтобы получить хорошее представление о том, как запланировано выполнение
Там нет проблем с пользовательским интерфейсом или что-нибудь. Ваш код работает. Ваше ожидание неверно.
С использованием ExtendedExecutionSession
Вы говорите своему приложению, что вам нужно время для сохранения, и оно не будет отменено, пока вы не закончите. В вашем случае это занимает около 9 секунд.
Попробуйте приостановить приложение, подождите 10 секунд и затем отмените его. Это произойдет немедленно. Затем попробуйте приостановить приложение и отозвать его до завершения сеанса. Теперь ExtendedExecutionSession
сообщит вашей ОС, что ваше приложение еще нельзя отозвать, и оно должно дождаться завершения процесса сохранения. Это то, что ты хочешь.
См. Документ Microsoft для расширенного выполнения:
Запрос
ExtendedExecutionReason.SavingData
расширенный сеанс выполнения, когда приложение находится в состоянии приостановки, создает потенциальную проблему, о которой вам следует знать. Если в состоянии приостановки запрашивается расширенный сеанс выполнения, и пользователь запрашивает повторное запуск приложения, запуск может занять много времени. Это связано с тем, что период времени расширенного сеанса выполнения должен завершиться до того, как старый экземпляр приложения может быть закрыт, а новый экземпляр приложения может быть запущен. Время выполнения запуска пожертвовано, чтобы гарантировать, что пользовательское состояние не потеряно.
То, что упомянуто в разделе о "Отменить", вам тоже интересно:
Когда
Revoked
событие запущено дляExtendedExecutionReason.SavingData
расширенный сеанс выполнения, у приложения есть одна секунда, чтобы завершить выполняемую операцию и завершить приостановку.
Одной секунды недостаточно, чтобы закончить свои 9 секунд ожидания.
Чтобы исключить возможность отложенного отображения вашего отладочного вывода, вы можете проверить его, добавив текущее время к выходу. Возможно, у ОС проблема с закрытием сессии, потому что 9 секунд не заканчиваются.
Также обратите внимание на замечание на EnterBackground:
Ранее ваш приостановленный обратный вызов был лучшим местом для сохранения состояния после того, как пользователь завершил сеанс работы с вашим приложением. Однако теперь приложение может продолжить работу в фоновом режиме, а затем вернуться на передний план из-за активности триггера, даже не достигнув приостановленного состояния. Лучшее место для сохранения данных после сеанса пользователя - это введенный вами обработчик фоновых событий.
Возможно, вы захотите сделать свой код в случае, если Exiting
событие запущено.
За OnSuspending
попробуйте выполнить ожидание с циклом for, который прерывается (отменяя процесс сохранения), как только произойдет отмена, ожидая только полсекунды за раз.
ОБНОВИТЬ:
... Или используйте фоновую задачу, так как приостановка кажется единственным надежным предупреждением перед завершением:
//
// 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();
}
ОБНОВЛЕНИЕ 2:
Для "правильного" способа, как это должно быть сделано, см. Официальный пример:
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;
});
}