Extended execution not working properly?

后端 未结 2 1712
小鲜肉
小鲜肉 2020-12-18 19:25

I\'m not able to get ExtendedExecution to work properly. The problem is that the Revoked event is not being fired until the execution is finished.

相关标签:
2条回答
  • 2020-12-18 20:04

    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;
        });
    }
    
    0 讨论(0)
  • 2020-12-18 20:26

    The usage of the await and Task is causing that your continued Task remains on the main thread making you have to wait with the black screen. Remember that the await behavior is to schedule the execution into the Dispatcher, NOT to begin a new thread, NOR schedule its execution into the ThreadPool. As consequence, No more UI messages can be processed until the Delay() ends.

    Just perform your time-consuming operation on a new thread, but ensure to keep the session open until it ends.

    Take a look of this https://msdn.microsoft.com/en-us/magazine/jj991977.aspx to get a good insight about how execution is sheduled

    0 讨论(0)
提交回复
热议问题