2

I want my in-process background task to run when the app is killed. This is to constantly track the user's location changes. Not geofencing. So, is there a way to do this in UWP? Following is my code and it gets killed when the app is killed. It took only a few minutes to do this on Android (Sticky background service) & iOS. Using a WindowsRuntimeComponent with out of process task is not possible because I have a lot of code to refer from my Xamarin Forms PCL. The equivalent of what I'm looking for is Android Sticky Background Service.

private async void RegisterBackgroundTask() 
{
    var backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();

    if (backgroundAccessStatus == BackgroundAccessStatus.AllowedSubjectToSystemPolicy)
    {
        UnregisterTask(TaskName);
        RegisterTask(TaskName);
    }
}

private async void RegisterTask(string taskName)
{
    BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder
    {
        Name = taskName,
    };

    var trigger = new ApplicationTrigger();
    taskBuilder.SetTrigger(trigger);

    var registration = taskBuilder.Register();
    await trigger.RequestAsync();
}


protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
    Debug.WriteLine("Background " + args.TaskInstance.Task.Name + " Starting...");
    SendToast("Hi this is background Task");
    //
    // Query BackgroundWorkCost
    // Guidance: If BackgroundWorkCost is high, then perform only the minimum amount
    // of work in the background task and return immediately.
    //
    var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
    var settings = ApplicationData.Current.LocalSettings;
    settings.Values["BackgroundWorkCost"] = cost.ToString();

    //
    // Associate a cancellation handler with the background task.
    //
    args.TaskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);

    //
    // Get the deferral object from the task instance, and take a reference to the taskInstance;
    //
    _deferral = args.TaskInstance.GetDeferral();
    _taskInstance = args.TaskInstance;

    _periodicTimer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(PeriodicTimerCallback), TimeSpan.FromSeconds(1));
}

private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    //
    // Indicate that the background task is canceled.
    //
    _cancelRequested = true;
    _cancelReason = reason;

    SendToast("Background " + sender.Task.Name + " Cancel Requested...");
}

private void PeriodicTimerCallback(ThreadPoolTimer timer)
{
    if ((_cancelRequested == false) && (_progress < 100))
    {
        _progress += 10;
        _taskInstance.Progress = _progress;
        SendToast("Timer Went Off!!");
    }
    else
    {
        _periodicTimer.Cancel();

        var key = _taskInstance.Task.Name;

        //
        // Record that this background task ran.
        //
        String taskStatus = (_progress < 100) ? "Canceled with reason: " + _cancelReason.ToString() : "Completed";
        var settings = ApplicationData.Current.LocalSettings;
        settings.Values[key] = taskStatus;
        Debug.WriteLine("Background " + _taskInstance.Task.Name + settings.Values[key]);
        SendToast(taskStatus);
        //
        // Indicate that the background task has completed.
        //
        _deferral.Complete();
    }

}

public static void SendToast(string message)
{
    var template = ToastTemplateType.ToastText01;
    var xml = ToastNotificationManager.GetTemplateContent(template);
    var elements = xml.GetElementsByTagName("text");
    var text = xml.CreateTextNode(message);

    elements[0].AppendChild(text);
    var toast = new ToastNotification(xml);
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}
Heshan
  • 913
  • 8
  • 30
  • Why downvotes?? Whats wrong with the question???? – Heshan Mar 05 '17 at 13:53
  • Not a downvoter, though there is one question on my mind - are you trying to have a BackgroundTask without runtime component? – Romasz Mar 05 '17 at 19:03
  • @Romasz Yes. I'm trying to implement an in-process background task which was introduced in the anniversary update. https://learn.microsoft.com/en-us/windows/uwp/launch-resume/create-and-register-an-inproc-background-task – Heshan Mar 06 '17 at 02:37
  • Have you tried to test it without debugger attached? – Romasz Mar 06 '17 at 06:19
  • Yes. Suspend from life cycle events. Working. – Heshan Mar 06 '17 at 06:28
  • If you want the background code to run when app is killed, then I suppose you will have to use runtime component as a separate process. the MSDN says: `If the code running in an in-process background task crashes, it will take down your app.`, then I would assume that it works also back-way - when you kill the foreground app, the process is also killed. – Romasz Mar 06 '17 at 06:33
  • Hmmm. I'm not sure how they call this as a BackgroundTask then. If we need to do work while the app is minimized, we can use ExtendedExecution... – Heshan Mar 06 '17 at 06:55
  • I'm not sure if it should be killed along with the app - it's my assumption. I'm also not sure if it's a separate process with its own resources or it shares them with the app (when you open lifecycle tab - can you see other process that foreground in a list?), apart the fact it's a piece of code that can run when app is suspended. Maybe this is the next step after extended execution ([I've encountered some problems](http://stackoverflow.com/q/36105996/2681948) which made it unusable for me), if so, then IMHO it's a good one - it's clearer. – Romasz Mar 06 '17 at 07:01
  • Hmmm. Anyway, I can't seem to find a way to make this work. I can't use a runtime library because I need to refer methods from the Xamarin Forms PCL inside the background task. It's working perfectly on both Android & iOS. UWP still lagging behind... :/ @Romasz I really appreciate your input regarding this matter. – Heshan Mar 06 '17 at 07:42
  • I haven't played with this yet, will try in upcoming days if I find some time, though I cannot promise this. Will give you a sign if I get any conclusions. – Romasz Mar 06 '17 at 07:50
  • Thanks @Romasz. I appreciate that. – Heshan Mar 06 '17 at 07:51
  • Done. Deleted the first post. Also, I noticed that the downvotes are disappeared as well. :) – Heshan Mar 06 '17 at 08:12
  • They haven't disappeared - you have just got some upvotes. With your reputation you should be able to click on the number and see how many app/down votes the post has. IMHO there is little chance that downvotes disappear - most people doesn't come back to questions. – Romasz Mar 06 '17 at 08:17
  • I have 2 Questions: 1) I could not get the point of your code. It seems that... the background task just end after 10 sec from the time you registered the task. And, as you know, there is a 30sec CPU quota limit. How do you track the user location "constantly" by this code? 2) What do you mean "Kill" ? you mean that it's terminated by OS, as of ALM event or application error? or you mean that the user click the app close button? – Mamoru Satoh Mar 06 '17 at 09:10
  • 1) Yes. Background task ends after 10 seconds. This is just a test code I took from the official sample. If you want to keep the process alive, stop calling _deferral.Complete(). 2) Geolocator is being used to track location every 5Km which fires a Location changed trigger. Yes, Kill means user pressing the close button. – Heshan Mar 06 '17 at 09:43
  • 1) You can't. Unfortunately, background task have only 30sec CPU quota. If your task reached to 30sec, OS terminate your task without any notification even you don't call the _deferral.Complete. This is by design. Basically, "Background Task" in WinRT is NOT resident service. It is invoked by System Event - Timer(min interval is 15min), Timezone changed, network changed, etc -, and it's limited to 30sec to run. One exception is "Maintenance Trigger" - it can run for several minutes - but it need AC power. Not good for location data. You can refer the background tasks guideline for details. – Mamoru Satoh Mar 06 '17 at 12:20

0 Answers0