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?

  • The way this works in when your app starts, you register a temporal event.  Say every hour.  Then ever hour, the background runs.

    When it runs, it passes data byway of Background.exit().  Which is seen in onBackgroundData() in the app itself.

    Here's a blog post to get you started: https://developer.garmin.com/index.php/blog/post/guest-post-creating-a-connect-iq-background-service

    And a forum thread about backgrounding in general. (with an example WF.)

    https://forums.garmin.com/developer/connect-iq/f/discussion/5287/very-simple-sample-of-a-watch-face-with-a-background-process

  • If you take the sample WF in the thread I linked to and do little more than replace the view and make it a widget, you'll be able to see how this all works in the sim

    The background process here is really simple - return a string with the time, and the view is really simple, as all it really does is display that string and the actual time

  • Hi Jim, I read your guest post and your bgwf already. But the questions remain:

    What happens to the background service if the widget is started by user? Will it start another background service (the main app is started anyway, kills the current service implicitly?) 

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

    Furthermore I want to collect data each full hour. Probably a simple 5*60 don't work, isn't it?

  • Once the background is running, when you start the widget, basically nothing happens, except that onBackgroundData will be called if the background has run since the last time the widget was run.

    If you set the time to be say an hour (you can) The background just runs once an hour (if it can), and running the widget doesn't change that.

    "if it can" does come into play.  If you are running a CIQ device app, resources to run the widget's  background aren't available, but the background will run as soon as you exit the device app.

    If you are running the widget, and it's time for the background to run, it runs, so onBackgroundData may be called while the background is running.

    Here's a widget of mine where I use a background process to collect data (temperature in this case) and the frequency that the backround at is based on the scale of the graph.  For a 24hr graph, it runs every 30 minutes, while for a 12hr graph, every 15 minutes, etc.

    https://apps.garmin.com/en-US/apps/1af9fb3b-7f21-4f55-b4c3-0b946476a2ad

    Something else to consider is that by default, onBackgroundData will only see the data from the last time the background ran.  So in your background, you may want to use Background.getBackgroundData(); to combine data from other times the background ran but which hasn't been seen by the widget,

  • Hm, I tried it now, but I won't get it running. I tried to get a battery value every hour, actually tried to call it every 5 minutes in the simulator. Here is my app code:

    using Toybox.Application as App;
    using Toybox.Background;
    using Toybox.WatchUi as Ui;
    
    var canDoBG=false;
    var osDataPerHour = new [24];
    
    (:background)
    class BatteryInfoApp extends App.AppBase {
    
        function initialize() {
            AppBase.initialize();
            for (var i=0; i<24; i++) {
            	osDataPerHour[i] = App.getApp().getProperty(getHoursString(i));
            }
            System.println(osDataPerHour[21]);
        }
    
        // onStart() is called on application start up
        function onStart(state) {
        }
    
        // onStop() is called when your application is exiting
        function onStop(state) {
        }
    
        // Return the initial view of your application here
        function getInitialView() {
        	if(Toybox.System has :ServiceDelegate) {
        		canDoBG=true;
        		//Background.registerForTemporalEvent(new Time.Duration(3600));
        		Background.registerForTemporalEvent(new Time.Duration(300));
        	} else {
        		System.println("****background not available on this device****");
        	}
            return [ new BatteryInfoView() ];
        }
    
        function onBackgroundData(data) {
        	var now=System.getClockTime();
        	var hours = now.hour;
            System.println("onBackgroundData="+data+" at "+ hours);
    		App.getApp().setProperty(getHoursString(hours), data);
    		osDataPerHour[hours] = data;
            System.println(osDataPerHour[21]);
    		//App.getApp().setProperty(osDataPerHour[hours],data);
            //bgdata=data;
            //App.getApp().setProperty(OSDATA,bgdata);
            Ui.requestUpdate();
        }    
    
        function getServiceDelegate(){
        	var now=System.getClockTime();
        	var ts=now.hour+":"+now.min.format("%02d");    
        	System.println("getServiceDelegate: "+ts);
            return [new BgbgServiceDelegate()];
        }
    
    	function getHoursString(h) {
    		return "batteryAtHour" + h;
    	
    	}
    
    
    }

    and here the bg service:

    using Toybox.Background;
    using Toybox.System as Sys;
    
    // The Service Delegate is the main entry point for background processes
    // our onTemporalEvent() method will get run each time our periodic event
    // is triggered by the system.
    
    (:background)
    class BgbgServiceDelegate extends Toybox.System.ServiceDelegate {
    	
    	function initialize() {
    		Sys.ServiceDelegate.initialize();
    
    	}
    	
        function onTemporalEvent() {
        	//var now=Sys.getClockTime();
        	//var ts=now.hour+":"+now.min.format("%02d");
            //Sys.println("bg exit: "+ts);
            //just return the timestamp
            Background.exit(Sys.Stats.battery);
        }
        
    
    }
    

  • For 

    Background.exit(Sys.Stats.battery);

    where are you setting "Stats"?  I'm guessing the background is crashing and you're not seeing any data due to that.

  • In the second link, something to note in the code Brian posted, is that you could run into a problem based on how long it's been since the widget itself has run.  It keeps adding to an array, and at some point that array will be larger than you can return from the background.  So you want to add a check for the array size and prune the data (get rid of the oldest) at some point.

  • may be something like this (lets say every 1hr for 24h)

    if ( exitData.size()==24 ){
        exitData = exitData.slice(1,23);
        exitData = exitData.add(newData);
    }

    
    

  • Yes, something like that.  The exact size could vary based on how much data you are returning, so 24 could be too much, or you may be able to return 72,

    One thing I do is return a timestamp for each item (Time.now().value() is what I use as it's small being a simple number), because even if you schedule for once an hour, the background won't run if you're also running a CIQ device app so there could be gaps in the data.