Background task does note exit correctly ?

Has anyone had problems with background task not exiting correctly. The following test application works fine in the simulator but on my Fenix 5 the onBackgroundData function is not called.The strange part is that the function is called sometimes so it has to be something in my code.

using Toybox.Application;
using Toybox.WatchUi as Ui;
using Toybox.Time.Gregorian as Gregorian;
using Toybox.System as Sys;

var bkgAllowed = false;

class BkgTestApp extends Application.AppBase {

	var dateTimeString;
	var today;

    function initialize() {
        AppBase.initialize();
    	bkgAllowed = (Toybox.System has :ServiceDelegate and Toybox has :Background);
    }

    // onStart() is called on application start up
    function onStart(state) {
    }

    function onStop(state) {
    }

    function getInitialView() {

    	if (bkgAllowed) {
   			Toybox.Background.registerForTemporalEvent(new Toybox.Time.Duration(5 * 60));
   		}

        return [ new BkgTestView() ];
    }
(:background)
    function onBackgroundData(response) {
Sys.println("App: resp: " + response);    
		if (response instanceof Array) {
           	if (response[1]) {
				today = Gregorian.info(Time.now(), Time.FORMAT_MEDIUM);
				dateTimeString = today.hour + ":" + 
								 today.min.format("%02d") + " " +
								 today.day_of_week.toString().substring(0,3) + " " + 
								 today.day + " " + 
								 today.month + " " + 
								 today.year.toString().substring(2,4);		
           		Application.Storage.setValue("lastCharge", dateTimeString);
Sys.println("App: write charge: " + dateTimeString);
           		Ui.requestUpdate();
           	}
		} 
	}
	
(:background)
    function getServiceDelegate(){
        return [new BkgTestBkg()];
    }
}

using Toybox.Background;
using Toybox.System as Sys;

(:background)
class BkgTestBkg extends Toybox.System.ServiceDelegate {

	var response = [0.0, false];
	
	function initialize() {
		Sys.ServiceDelegate.initialize();
	}

    function onTemporalEvent() {
        response[0] = Sys.getSystemStats().battery;
        
        if (Sys.getSystemStats() has :charging) {
        	response[1] = Sys.getSystemStats().charging;
        } else {
        	response[1] = false;
        }

Sys.println("bkg: charging: " + response[1] + " bat: " + response[0]);
        Background.exit(response);
    }
}

using Toybox.Application as App;
using Toybox.WatchUi  as Ui;
using Toybox.System   as Sys;
using Toybox.Graphics as Gfx;

class BkgTestView extends Ui.View {

	var w2;
	var h2;
	var lastCharge = "";

    function initialize() {
        View.initialize();
        w2 = Sys.getDeviceSettings().screenWidth / 2;
        h2 = Sys.getDeviceSettings().screenHeight / 2;
    }

    function onLayout(dc) {
        setLayout(Rez.Layouts.MainLayout(dc));
    }

    function onShow() {
    }

    function onUpdate(dc) {
		dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_BLACK);
		dc.clear();
		dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_TRANSPARENT);

		if (bkgAllowed) {
        	lastCharge = App.Storage.getValue("lastCharge");

        	if (lastCharge != null and lastCharge.length() > 1) {
        		dc.drawText(w2, h2, Gfx.FONT_SYSTEM_XTINY, "Last charge:",  Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
        		dc.drawText(w2, h2 + Gfx.getFontHeight(Gfx.FONT_SYSTEM_XTINY)*1, Gfx.FONT_SYSTEM_XTINY, lastCharge,  Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
        	} else {
        		dc.drawText(w2, h2, Gfx.FONT_SYSTEM_XTINY, "no charge info found",  Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
        	}
        } else {
        	dc.drawText(w2, h2, Gfx.FONT_SYSTEM_XTINY, "device not supported",  Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
        }
    }

    function onHide() {
    }

}

  • If you're running on a real device and charging using a pc/mac, the background process doesn't run during that time.  It's the same if you are running a ciq device app on a real device - the background for something like a widget or watch face doesn't run while the device app is running.

  • Thanks Jim, I get that the background tasks don’t run whilst connected to PC but this sample code still doesn’t always call the callback function to return data. The trace file shows the function called every 5 minutes but it does not always get back to the function handling the response. 

  • The main app will only see the data in onBackgroundData if it's running.  So in the case of a widget the background could be running every 5 minutes for an hour, but the widget only sees the data from the last time the background runs.

    if you want to prevent losing the result each time the back runs, in the background, use getBackgroundData() and append the latest data to what's already there.  Something like this.  In this case I limit the size returned to maxSize*2

        function onTemporalEvent() {
        	var old=Background.getBackgroundData();
        	if(old==null) {old=[];}
        	old.add(Time.now().value());
        	old.add(Sys.getSystemStats().battery);
    		var sz=old.size();
            if(sz>maxSize*2) {
            	old=old.slice(2, sz);
            }
            Background.exit(old);
        }

    onBackgroundData() doesn't run in the background, but runs in the main app.  So it doesn't run each time Background.exit() is called.  Only when the main app is running.  In the sim, your main app will be running when the background runs.

  • Jim, That makes perfect sense, not sure why I didn't work that out myself. Seems obvious now. Yep, I have a way to work around it in my widget now I know what is happening. Thanks!