0

I'm syncing files in UWP APP. I'm using a background task and ApplicationTrigger. If I debug my code (I mean if a debugger is attached) it works, but if I run installed application, the background task is terminated after 10-20 minutes. I have to sync a lot of files and it will take 1-2h (I know it's crazy). I found info here: https://msdn.microsoft.com/en-us/windows/uwp/launch-resume/handle-a-cancelled-background-task, but I'm not sure if this is it, because everything with memory is OK.

"Note For all device families except desktop, if the device becomes low on memory, background tasks may be terminated. If an out of memory exception is not surfaced, or the app does not handle it, then the background task will be terminated without warning and without raising the OnCanceled event. This helps to ensure the user experience of the app in the foreground. Your background task should be designed to handle this scenario."

    public async void Run(IBackgroundTaskInstance taskInstance)
    {
        deferral = taskInstance.GetDeferral();
        _taskInstance = taskInstance;

        var details = taskInstance.TriggerDetails as ApplicationTriggerDetails;

        IEnumerable<string> filesUrls = details.Arguments.Select(x => x.Value as string).Distinct().ToList();
        filesCount = filesUrls.Count();

        downloader = CompletionGroupTask.CreateBackgroundDownloader();

        var result = await Download(filesUrls, taskInstance, downloader);
        if (result)
        {
            await Download(failedDownloads, taskInstance, downloader);
        }
        downloader.CompletionGroup.Enable();
        deferral.Complete();
    }

    private async Task<bool> Download(IEnumerable<string> filesUrls, IBackgroundTaskInstance taskInstance, BackgroundDownloader downloader)
    {
        bool downloadFailed = false;
        failedDownloads = new List<string>();
        foreach (string url in filesUrls)
        {
            DownloadOperation download = null;
            var uri = new Uri(url);
            try
            {
                download = downloader.CreateDownload(uri, await CreateResultFileAsync(url.Split('/').Last()));
                Task<DownloadOperation> startTask = download.StartAsync().AsTask();
                await startTask.ContinueWith(task => OnDownloadCompleted(task, url));
            }
            catch
            {
                downloadFailed = true;
                failedDownloads.Add(url);
            }
        }
        return downloadFailed;
    }


    private void OnDownloadCompleted(Task<DownloadOperation> task, string url)
    {
        if (task.Status == TaskStatus.RanToCompletion)
        {
            completedDownloads++;
            decimal progress = (completedDownloads / filesCount) * 100;
            _taskInstance.Progress = Convert.ToUInt32(progress);

        }
        else if(task.Status == TaskStatus.Faulted)
        {
            failedDownloads.Add(url);
        }
    }

    private async Task<IStorageFile> CreateResultFileAsync(string fileName)
    {
        var local = ApplicationData.Current.LocalFolder;
        IStorageFile resultFile = await local.CreateFileAsync(fileName, CreationCollisionOption.FailIfExists);
        return resultFile;
    }
}

Does anyone know why my task is killed?

Mario Boss
  • 1,784
  • 3
  • 20
  • 43
Ania
  • 65
  • 7
  • I had a similar issue a while back. adding `task.ContinueWith(t => 'some logging here', TaskContinuationOptions.OnlyOnFaulted);` help me pinpoint it. – MX D Apr 19 '16 at 14:03
  • But it helped you pinpoint what? What was the problem? Could you tell me more about your issue? – Ania Apr 19 '16 at 14:13
  • For me it was a Task I ran inside of my task that silently failed and caused the thread to abort. That failure was caused by a file access exception. Not sure why it did not appear as a `aggregated-exception` instead. But I believe it had something to do with unhandled exceptions that get collected/finalized. Which will kill your process – MX D Apr 19 '16 at 14:33
  • Can you confirm your task is getting cancelled. Place a taskInstance.Canceled event in the Run method and create a log with a timestamp to record that this has happened. Also, is your app still running? If it is then it unlikely to be an exception causing the issue. – 27k1 May 01 '16 at 15:04

2 Answers2

3

Updated for the Creator's Update

There is now a restricted capability called extendedBackgroundTaskTime that can be used to enable a background process to run for 1 to 2 hours if needed. It will not be accepted into the Windows Store but works for side-loaded Line-Of-Business apps and submission into the Windows Store for Business: https://learn.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations

There is currently no method for running a truly headless background sync for 1 hour to 2 hours for an app submitted to the Store. That type of sync could be done asynchronously and in chunks. The BackgroundUploader and BackgroundDownloader classes are options to do the network transfer of individual files and then be woken up to do work based on the download completion of each of those files. If the sync can wait until the device is in an AC-connected state, then Maintenance Trigger can also be registered to periodically wake up and run for 10 minutes to do a chunk of syncing work.

If your app can be in the foreground and minimized then Extended Execution Unspecified may be a good option. It is also battery-aware, but if they app is in an AC-connected state and isn't trying to enter Connected Standby, then it could run indefinitely. This method is generally used for media or other project compilation activities with multitasking, but may be an approach you could take.

chcortes
  • 116
  • 4
1

Your background task gets cancelled with BackgroundTaskCancellationReason = ExecutionTimeExceeded because the maximum time allowed for a background task with ApplicationTrigger to run is 10 minutes if the app is running. If the app is suspended, a background task with ApplicationTrigger is allowed to run for a maximum of 5 minutes. If this time limit is reached, the OS will cancel the task with BackgroundTaskCancellationReason = IdleTask.

In your background task put this code at the beginning of the Run method to see the cancellation reason in a toast.

        taskInstance.Canceled += (s, e) => {
            var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
            var toastTextElements = toastXml.GetElementsByTagName("text");
            toastTextElements[0].InnerText = e.ToString();
            ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
        };
Vitaly
  • 346
  • 2
  • 10