Background : Crossing the firewall.

Hi,

Supposing I have a background service, calling at intervals, and I want to toggle between calling service a) and service b) - one at a time every once in a while is fine.

The ideal might be to just swap out the service delegate, but that doesn't seem to be possible. 

Another approach might be to pass values to the service delegate, but again, I can't see a way to do that. I haven't tried just setting globals yet, but that seems a bit wrong!

The third approach is to code the whole thing into one service delegate with all kinds of logic in the onTemporalEvent but that hardly seems like the right thing to do!

Am I missing something or am I attempting the impossible?

G

  • With a background you have the every 5 minute max.

    But since background code is available to the main app, I actually call the comm part from the main app.  I have a class called "MyComm" for example.

    (:backround)

    Class MyComm {

    function makeRequest(...) {

    }

    function handleResponse(...)  {

    }

    }

    You can use it in both the background and foreground.  I do this on widgets for devices that don't even allow backgrounding.

  • That's pretty much what I was trying, but I am clearly doing something wrong and / or different.

    You aren't extending ServiceDelegate for MyComm, is that correct? EG: Are you calling "MyComm" from the ServiceDelegate?

    Specifically, the problems I'm seeing right now are like this:

    // IN APP BASE INITIALISE, DECLARE A SERVICE DELEGATE
    (:background)
    class WF_ServicesApp extends Application.AppBase {
        var svcDel;
        function initialize() {
            AppBase.initialize();
            svcDel = new ServiceManagerDelegate();
            
    // RETURNING SAME IN getServiceDelegate()
        function getServiceDelegate() {
        	return [svcDel];
        }
        
    // INSIDE SERVICE DELEGATE
    var nextUrl;
    	var nextPayload;
    	var nextOptionsPost;
    	var nextRequestIdentifier;
    	function getJSON(identifier,url,payload,isPost) {
    		nextRequestIdentifier = identifier;
    		nextUrl = url;
    		nextPayload = payload;
    		nextOptionsPost = isPost;
    		Toybox.System.println("REGISTERED POST "+nextRequestIdentifier+" WITH\n"+nextUrl+"\n"+nextPayload+"\n"+(nextOptionsPost?"post":"get"));
    		startTemporal();
    	}
    	function onTemporalEvent() { //A callback method that is triggered in the background when time-based events occur.
    		var options =  {
    		   :method => nextOptionsPost ? Communications.HTTP_REQUEST_METHOD_POST : Communications.HTTP_REQUEST_METHOD_GET,
    		   :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
    		};
    		Toybox.System.println("REQUST IS \n"+nextUrl+"\n"+nextPayload+"\n"+options+"\n"+method(:responseCallback));
    		Communications.makeWebRequest(nextUrl, nextPayload, options, method(:responseCallback));
    	}

    From console logs, I then see:

    REGISTERED POST kpay WITH
    https://testurl.url/api/
    {accounttoken=>"STUFF"}
    get
    Background: init
    Background: onStart
    Background: CALLING GET SERVICE DELEGATE 
    Background: REQUST IS 
    null
    null
    {symbol (8388786)=>1, symbol (8389844)=>0}
    Obj: 148
    
    Error: Unhandled Exception
    Exception: UnexpectedTypeException: Expected String, given null
    Stack: 
      - makeWebRequest() at T:\mbsimulator\submodules\technology\monkeybrains\virtual-machine\api\Communications.mb:385 0x30000e1f 
      - onTemporalEvent() at E:\gdcsoft\SVN_GARMIN\WF_Services\source\WF_ServicesApp.mc:122 0x100005d4 
    

    EG: The values that I have passed to svcDel are clearly set (and verified in console) but are not available (null) when I actually do the Comms request.

    I mean... I could be doing something daft here, but I can't see where or what.

  • Stripped down version of what I do:

    	(:background)
    class WuServiceDelegate extends Sys.ServiceDelegate {
    	var Wub;	
    ...
    	function onTemporalEvent() {    
    	    Wub=new WComm();
    	    //get location					    
    	    if(loc!=null) {	    	
    	    	Wub.doRequest(loc,method(:receiveW));
    	    }
    	}
    
    	function receiveW(responseCode, data) {			
    	    if(responseCode==200) {		
    		Background.exit(data);			
    	    } else {	
    		Background.exit(responseCode);
    	    }
    	} 
    }
    
    (:background)
    class WuComm {
    	var callback;
    	
    	function initialize() {
    	}
    	
    	function doRequest(loc,cb) {
    	    callback=cb;
    	    //do the makeWebRequest, with method(:receiveStation) as the call back;
    	}
    
    	function receiveWeather(responseCode, data) {
    	    callback.invoke(responseCode,data);
    	    callback=null;
    	}
    }

  • So when the background runs, I see the results by way of onBackgroundData()

    But the main app can also call

    Wub2.doRequest(loc,method(:receiveW2));

    (Wub2 is like Wub, but done in initialize in the main app)

    and see the response in receiveW2()

  • The problem was actually more basic than that!

    I was stuck at:

    // get location

    But I see from some sample projects people were using app.getProperty() to pass data about. Looks a bit weird to need that, but it works.

  • And cannot use symbols in the response data! 0_0

  • I typically use Application.Storage and/or Application.Properties on devices that support it.  And in the background, remember you can only read them, not write them. 

    In the case of a lat/lon I'll often fall back to Activity.Info.currentLocation.

  • I generally return the dictionary I get back from the makeWebRequest.

    In the code I posted, another trick is if there is an error (non 200), I return that instead of the response for onBackgroundData() so the main app can act on the specific error.