Background widget

Hi,

I have some questions about background processing: I want to design a widget which collects data on a hourly basis. What happens to the background service if the widget is started by user? Will it start another background service which mewns I have to kill the previous?

What happens with the background service if its triggered but currently the widget ie displayed? Will it executed anyway?

  • You can't use :symbols in data passed into Background.exit.Try to replace :batAtChargeUnplug with string key (like "b" or "batAtChargeUnplug").

    Think as data is a JSON and symbol is not json-serializable.

  • Wow, that worked. Thanks for that. Just some other questions:

    - Is there a trick how to store data in the background process which stays there even when the main app pulls the data from the exit statement? I tested a bit and realized that the background service is completely initialized on the event. Furthermore (as Jim wrote already in the guiest post) a storage to the object store throws an error

    - How can I test the widget's background process? If I end the widget with the up key and fire an event the simulator has completey unloaded the code...

  • If I recall, there was some way to have the background run based on a temporal even even if the widget itself wasn't.  But when I did this widget: https://apps.garmin.com/en-US/apps/1af9fb3b-7f21-4f55-b4c3-0b946476a2ad, I found it easier to test on a device and use println calls and an app log file.  In that one, the newest internal temp (from SensorHistory) didn't change, which was another reason to test on a real device.

  • Just another question to the background process:

    What happens if someone has just installed the widget without calling it or someone has done a hard reset? Is the background process called again for itself?

  • By default, when you first install a widget, you haven't registered a temporal event, but when you do so, the background will run as soon as you do that. (I think when most people install something new they look at it/try it right away so probably not an issue).  But I think (I've not tried this) you can do the register in onAppInstall(), so it's registered right away.

    If by "hard reset" you mean holding the light button until the watch turns off, the schedule for running the background should be intact when you turn it back on.

    update:  One thing to mention is if you are adding a "glance view" to a widget, you may want to be sure you add the register there, and not just in the full screen view.

  • I still have problems to get this running - it seems that the background process is not running and not collecting data. Maybe you want to review what I did and give some feedback:

    First the main app:

    using Toybox.Application    as App;
    using Toybox.WatchUi        as Ui;
    using Toybox.Background;
    
    
    var bgData;
    
    (:background)
    class BatteryInfoApp extends App.AppBase {
    
    
        function initialize() {
            AppBase.initialize();
        	bgData = {};
        }
    
    
        // Return the initial view of your application here
        function getInitialView() {
        	if(Toybox.System has :ServiceDelegate) {
        		Background.registerForTemporalEvent(new Time.Duration(300));
    
        	} 
            return [ new BatteryInfoView(), new batteryInfoDelegate()];
        }
    
        function onBackgroundData(data) {
    		bgData = data;
            Ui.requestUpdate();
        }    
    
        function getServiceDelegate(){
            return [new BgbgServiceDelegate()];
        }
    
    
    
    }

    And in the background process:

    using Toybox.Background;
    using Toybox.System as Sys;
    using Toybox.Time           as Date;
    using Toybox.Time.Gregorian as Greg;
    
    
    (:background)
    class BgbgServiceDelegate extends Toybox.System.ServiceDelegate {
    	
    	function initialize() {
    		Sys.ServiceDelegate.initialize();
    	}
    	
    	
        function onTemporalEvent() {
           	var data = Background.getBackgroundData();
    		if (data == null) {
    			data = {};
    		}
            var stats = Sys.getSystemStats();
           	if (stats.charging) {
           		data.put("batAtUnplug", stats.battery);
           		data.put("timeAtUnplug", getTimestamp());
           	}
            Background.exit(data);
        }
        
        function getTimestamp() {
        	return Date.now().value();
        }
    
    }
    

    The global data bgData is processed in the view:

    	function onShow() {
     		if (bgData != null) {
    			if (bgData["batAtUnplug"] != null) {
     				batData.setUnplugData(bgData["timeAtUnplug"], bgData["batAtUnplug"]);
     			}
     		}
     		
      		if (System.getSystemStats().charging) { // check if it is still charging
      			batData.setUnplugData(batData.now.value(), batData.curBat);
     		}
    	}
    
    

    Can you find anything in the code which prevents the watch from updating, if the plug is removed from the wall?

  • put in some println calls (maybe with a simple timestamp) and create a <yourapp>.txt log file on the watch to see when things are happening.

    Oh wait.  Something does jump out at me.  What you're doing in onShow() should probaby be done in onUpdate().  That's what gets called when you do the Ui.requestUpdate() in onBackgroundData. 

  • I have a widget that does something similar to this with a background event returning information every 5 minutes. It works fine in the simulator however on my Fenix5 test device the Background.exit(data) does not always callback to the onBackgroundData(data) function in the main app. The first line of my function is a debug println and it only seems to appear every 2-3 hours instead of every 5 minutes where as the onTemporalEvent function is being called and producing debug to indicate it is being called. Is there something I have done wrong registering the background temporal event ? It is odd that it would work in sim but not real device ? Thanks

  • Arrrgh, Jim, I need your help again. I have designed a new widget and can't get backgrounding running. I don't do different things as in my other widget where it's running perfectly. 

    I putted some println statements in my code. The service is initialized perfectly as I can see the println. Nevertheless, as soon as I fire a temporal event from the simulator my onTemporalEvent() function is not called (since I don't see the println). Consequently the onBackgroundData(data) function in the main app is not called. To be honest, I have no clue why the onTemporalEvent function is not called at all. Do you have any debug hints?