onBackgroundData not called

I searched the forum, and read all the old threads related to onBackgroundData, but I can't find out why it's not working.

(:background)
class MyServiceDelegate extends System.ServiceDelegate {

	function initialize() {
		Background.exit("this works");
	}
	
	function onTemporalEvent() as Void {
		log("onTemporalEvent");
		// Background.exit("test1");
		doTheWork();
		// Background.exit("test2");
	}
}

My problem is that onBackgroundData is not being called after I call Background.exit(non null String or Number); The only place where it works is when I call it from the constructor of the ServiceDelegate, which of course makes no sense, because then the job didn't even start yet, but at least it proves that the onBackgroundData in the foreground app is OK.

When I call Background.exit("str") from anywhere else (my real place is the onReceive callback of makeWebRequest, but even if I call it from the beginning of onTemporalEvent) onBackgroundData is not called. What can be the culprit?

SDK 4.1.7, simulator, fr255, datafield

  • first line, there was the comma inside ()

    (:background) //------> , :background_app)

    class StatsServiceDelegate extends System.ServiceDelegate {

        function initialize() {

            // TODO: can it be removed?

            ServiceDelegate.initialize();

            log("StatsServiceDelegate.initialize");

        }

        

        function onTemporalEvent() as Void {

            log("StatsServiceDelegate.onTemporalEvent");

            Stats.onTemporalEvent();

        }

    }

  • Here is an ultra simple DF with a background.  Every 5 minutes, it gets the time and returns the minute with Background.exit().  Noting fancy.

    It runs fine.  onBackgroundData() runs when it should, and the DF updates to show the value.

    It's something odd in your code.  I've used backgrounding on a number of apps for years with no issue, as have many other developers.

    dfmin.zip

    The println() in onBackground data shows what was sent with Background.exit()  Here's the last three times:

    data=41
    data=46
    data=51

  • In  code is comma in annotation

    (:background , :background_app)

    instead of

    (:background :background_app)

  • You're not assuming things like globals are shared between the main app and the background are you?  If you change a global in the main app, the background doesn't see the change, and in the same way, if the background changes a global, the main app doesn't see that change.

    In the code I posted, I use isBackground in AppBase.  I change it when running in the background, but only the background ever sees it as true.

  • But for those who actually read the thread, Gavriel is specifically having problems with calling Background.exit() from the response callback for makeWebRequest(). Also, IMO, just bc something is obviously different about his code, doesn't mean it's "wrong". If anything, I'd be curious to know what's different about his code that causes such a simple use case to fail.

    I tried his project out and was able to recreate the same behavior. Also, if I moved Background.exit() out of the makeWebRequest() callback, it worked for me (as it did for Gavriel).

    However, with some tweaks I could get his project work as intended, but not reliably. Every time I tried to refine a tweaked version of the code to isolate what the "real" fix was, it stopped working again.

    1) I reduced the number of calls in the stack by getting rid of Stats.onTemporalEvent(), moving the body of that function into StatsServiceDelegate.onTemporalEvent(), and moving Stats.send() to StatsServiceDelegate.

    2) It also seemed to help if I got rid of the two log() calls in onReceive()

    3) I also tried eliminating calls to Storage but have no idea if that did anything

    Unfortunately I'm back at the state where it doesn't work so it's possible all of that stuff was just a placebo.

    I do think there a few complicating factors in the project such as:

    - apparently in some cases, makeWebRequest will be called twice (at least in the sim) during the same invocation of the background process, but the callback is only called once (because it calls Background.exit())

    - calls to Storage and System.out.println() may be introducing weird timing issues.

    So instead of trying to "fix" his project, I tried to make a minimal example that does a makeWebRequest() to the same URL and calls Background.exit() in the response callback.

    Here's the code for a simple data field project:

    (All the app code is in one file to make it easier to post.)

    import Toybox.Activity;
    import Toybox.Application;
    import Toybox.Background;
    import Toybox.Lang;
    import Toybox.Time;
    import Toybox.WatchUi;
    import Toybox.System;
    
    (:background)
    class BgTestApp extends Application.AppBase {
        function initialize() {
            AppBase.initialize();
        }
    
        // Return the initial view of your application here
        function getInitialView() as Array<Views or InputDelegates>? {
            Background.registerForTemporalEvent(new Time.Duration(5 * 60));
            return [ new BgTestView() ] as Array<Views or InputDelegates>;
        }
    
        function getServiceDelegate() {
        	return [new BgTestService()] as Array<System.ServiceDelegate>;
        }
    
        function onBackgroundData(data) {
        	System.println("FOREGROUND: onBackgroundData(" + data + ")");
        }
    }
    
    function getApp() as BgTestApp {
        return Application.getApp() as BgTestApp;
    }
    
    (:background)
    class BgTestService extends System.ServiceDelegate {
    	function initialize() {
    		ServiceDelegate.initialize();	    	   
    	}
    	    
        function onTemporalEvent() as Void {
        	Communications.makeWebRequest(
                "https://api.ipify.org",
                {
                    "c" => "x"
                },
                {
                    :method => Communications.HTTP_REQUEST_METHOD_GET,
                },
                method(:onReceive)
            );
            System.println("BACKGROUND: makeWebRequest()");
    	}
    
        function onReceive(responseCode as Number, data as Dictionary or String or Null) as Void {
            Background.exit(responseCode);
        }
    }
    
    (:typecheck(disableBackgroundCheck))
    class BgTestView extends WatchUi.SimpleDataField {
        function initialize() {
            SimpleDataField.initialize();
            label = "My Label";
        }
    
        function compute(info as Activity.Info) as Numeric or Duration or String or Null {
            return 0.0;
        }
    }

    This is the output I get when I run the app in the sim (fr255m) or trigger a temporal event:

    Background: BACKGROUND: makeWebRequest()
    FOREGROUND: onBackgroundData(200)

    Works pretty reliably for me.

    I think maybe this part of the docs might be relevant:

    https://developer.garmin.com/connect-iq/api-docs/Toybox/System/ServiceDelegate.html

    ServiceDelegate is a class used to service Background events.

    This class is used as the main entry point for background processes. A callback function within the delegate can be used to initiate other system events (e.g. Communications), but only the delegate function is guaranteed to complete. The Background process may be shut down at any time to handle higher priority processes.

    I know that in Gavriel's case it doesn't appear like the background service is being prematurely shut down (based on the console logs), but *something* isn't running to completion/success (the part about returning background data to the main process.)

  • OK, there is some improvement: The culprit is onAppUpdate(). Until now I didn't call AppBase.onAppUpdate(), so I tried to add it

        (:background)
        public function onAppUpdate() as Void {
            log("onAppUpdate1");
            AppBase.onAppUpdate();
            log("onAppUpdate2");
            myCode();
        }

    When I call AppBase.onAppUpdate(), then it stops the background app (the log in the next line isn't called), this obviously also means that myCode() isn't called.

    So I thought I can move AppBase.onAppUpdate() to be called after myCode(). But even that is not really working, because myCode calls a webRequest, and because AppBase.onAppUpdate() stops the background app, it doesn't wait for the onReceive callback to be called.

    However, after calling AppBase.onAppUpdate() the onBackgroundData is called after onTemporalEvent.

    So it looks like either a bug in the simulator or in the documentation (webRequests don't work in onAppUpdate, onAppInstall)

  • I've use Background.exit() in the callback from a makeWebRequest for years and it's worked consistently  Not just a test app, but real production ones.

    It's something else

  • Ok, you don't call AppBase.onAppUpdate().  The system calls that. (in the background)

    Just add this to dfminApp.mc in the project I posted earlier to see how it works

    .

        function onAppUpdate() {
        	System.println("app update");
        	Background.exit(99);
        }
    .

    if "mycode()" does a makeWebRequest(), onAppUpdate is over before the callback is even called.so you won't see a response.

    update.  Actually, in thinking about this there may be a way to do a makeWebRequest and do a Background.exit() but to try it will take some time.  Maybe yet this weekend....

  • I've use Background.exit() in the callback from a makeWebRequest for years and it's worked consistently  Not just a test app, but real production ones.

    It's something else

    I never said you couldn't do that in general. I said that's the specific problem Gavriel was having: in *his* app, Background.exit() in a makeWebRequest doesn't work. I also posted a small sample app where it does work.

    So as you said, it is something else. (I also never disagreed with that.) But whatever that something else is, it only affects the case with Background.exit() in the makeWebRequest callback.

  • His code works on condition the comma will be removed from annotation:

    (:background , :background_app)

    class StatsServiceDelegate extends System.ServiceDelegate

    ...

    I don't know what compiler does in such case but surely code doesn't run well.