Reputation: 11
I'm developing a UWP app which uses in-process background task to download new GitHub notifications and show them as toast notifications. I'm using MVVM light. I tried In-process background tasks with TimeTrigger, everything works as expected when the app is in foreground or minimized. But when the app is closed, the background tasks stop working. After monitoring task manager, I found that the background task launches but is closed immediately just after launching. So i used ExtendedExecutionSession to extend the suspension. Still it isn't working. Using Out of the process background tasks resolves the previous problem but introduces a new problem. The GitHub api call failed in the background due to inaccessibility to the app data (GitHub Auth Data) which is required to make the API call.
In short, I have the following problems:
I've found these workarounds:
My questions are:
How and when should I call ExtendedExecutionSession in In-process tasks.
How to code Inter-process communication in order to be able to access the app data from the background app
Note that I've registered the background tasks in MainPage.xaml.cs. Here is the code snippet:
var syncBuilder = Helpers.BackgroundTaskHelper.BuildBackgroundTask(
"SyncNotifications",
new TimeTrigger(15, false),
internetAvailableCondition,
userPresentCondition,
sessionConnectedCondition
);
syncBuilder.IsNetworkRequested = true;
syncBuilder.Register();
And I overridden the OnBackgroundActivated in the App.xaml.cs. Here is the code spippet:
var deferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Task.Completed += BackgroundTask_Completed;
var taskName = args.TaskInstance.Task.Name;
switch (taskName)
{
case "ToastNotificationBackgroundTask":
await CoreApplication
.MainView
.CoreWindow
.Dispatcher
.RunAsync(
CoreDispatcherPriority.Normal,
async () =>
{
var toastTriggerDetails = args.TaskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;
var toastArgs = QueryString.Parse(toastTriggerDetails.Argument);
var notificationId = toastArgs["notificationId"];
if (!notificationId.IsNulloremptyOrNullorwhitespace())
{
await NotificationsService.MarkNotificationAsRead(notificationId);
}
});
break;
case "SyncNotifications":
await CoreApplication
.MainView
.CoreWindow
.Dispatcher
.RunAsync(
CoreDispatcherPriority.Normal,
async () =>
{
AppViewmodel.UnreadNotifications = await NotificationsService.GetAllNotificationsForCurrentUser(false, false);
await AppViewmodel.UnreadNotifications?.ShowToasts();
});
break;
}
Upvotes: 1
Views: 1810
Reputation: 13850
The problem in your code is that you are completing the Deferral too early, i.e. before your asynchronous dispatcher work items had a chance to execute. This happens to work when the app is in the foreground because the process will stay around anyway, but when activated in the background the process will only run until you mark the deferral as complete, meaning it will exit before your task code had a chance to run.
To see what I mean, try a little experiment. Run this code that mimics your current implementation under the debugger and watch in which order the breakpoints are being hit. This will help you realize that you are calling Deferral.Complete before your actual code gets to run:
protected async override void OnBgActivated()
{
await CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await PerformTaskAsync();
Debugger.Break();
});
Debugger.Break();
}
private async Task PerformTaskAsync()
{
await Task.Yield();
Debugger.Break();
return;
}
Now the solution for you is simple, move the Deferral.Complete() call to a place in your code after your task is actually complete. You may have to change the scope of 'deferral' for this and make a class-level variable.
Upvotes: 1