How to determine background state prior to "getServiceDelegate"?

When using the older API calls for properties access: App.getApp().setProperty(...) I could determine if the app is in background-state at startup like so, because "setProperty" in background throws an exception that can be caught:

(:background)    
function isBackground() {
    try {
        App.getApp().setProperty("dsfg94339fj2e9485hduth3", false);
        App.getApp().deleteProperty("dsfg94339fj2e9485hduth3");

        return false;
    } catch (ex) {
        return true;
    }
}

I can then use it at startup e.g. like so delegating all calls to the appropriate controllers:

(:background)
class MyApp extends Application.AppBase {

    private var appRunner = null;

    function initialize() {
        AppBase.initialize();
        if (isBackground()) {	
            self.appRunner = new MyAppBackgroundController();
        } else {
            self.appRunner = new MyAppForegroundController();
        }
    }

    function onStart(state) {
        return self.appRunner.onStart(state);
    }
    
    // ...
    
}

Whenever only the new API calls are possible (App.Storage.get/set, App.Properties.get) I cannot leverage the exception anymore, because setting storage from background is now permitted. Do you know of any other way to determine whether or not an app is in background at initialization?

Things I considered:

  • "getServiceDelegate" which is only called by background is too late for determination because then "onStart" already happened.
  • Illegal Access (out of Bounds) when calling a non-background function from background is an error and not an exception so I cannot catch it
  • Module access error (like Toybox.Communications) is also an error and not an exception so not catchable
  • The solution is fairly simple:

    Although you can set storage values from background since API lvl 3.2.0 you cannot set existing property values from background.

    So I just created a dummy property in my properties.xml, e.g.:

    <properties>
        <property id="BackgroundDummy" type="boolean">false</property>
    </properties>

    And then changed the "isBackground" function to use the new Properties API, while also covering the case that the API level in use does not offer the newly available App.Storage, App.Properties access:

    (:background)    
    function isBackground() {
    
        try {
            if (Toybox.Application has :Storage) {
                App.Properties.setValue("BackgroundDummy", true);
            } else {
                App.getApp().setProperty("dsfg94339fj2e9485hduth3", true);
                App.getApp().deleteProperty("dsfg94339fj2e9485hduth3");
            }
            return false;
        } catch (ex) {
            return true;
        }
    }

    For now this solution works for me,

    although I still would prefer to not rely on a specifically created dummy property

  • It can be simpler, and not use properties.  Understand your AppBase runs for both the main app and background.  But at the same time, some of the callbacks run for both, some for only the main app, and some for only the background.  And that the main app and background have their own copies of a variable.  Consider this:

    class MyBase extends Application.AppBase {
        var inBackground=false;
        
        function initialize() {
            AppBase.initialize();
            //runs for BG and FG
        }
        
        /...
        
        function onStop(state) {
        //check if BG or FG.  Each has their own version of inBackground
            if(inBackground) {
                //do what's needed in BG
            } else {
                //do whats needed in FG
            }
        }
        
        function onBackgroundData(data) {
            //only runs in FG
        }
        
        function getServiceDelegate() {
            //only runs in BG and has it's own copy of inBackground
            inBackground=true;
        }
        
    }

    No properties used.  Just a variable in your AppBase.

  • Thanks for your answer, I know about the solution with getServiceDelegate (see "Things I considered" at the bottom of my question), but this is unfortunately called too late, I want to know if it is background at initialization so e.g. "onStart" can distinguish between foreground and background.

  • Something you might try is using System.getSystemStats().totalMemory in initialize().  and check if it's the amount of the background or the foreground.  and set inBackground accordingly.

  • Is there a documentation where the available memory for foreground/background apps for each app type for device is listed?

  • You can find it in compiler.json file for a device or just put a println for totalMemory in something you know is running in the background 32k/28k is common but here's what you see in the compiler.json for the 255 as an example.

    {
    "memoryLimit": 65536,
    "type": "background"
    },

    Remember, what your app sees is actually 4k less that what you see in compiler.json (that's why I said 32/28k)

  • I'm wander why do you need this information in onStart?

    FOR and BAG are separated processes no direct exchange data between them.

    So only a little problem is with onBackgroundData because it's called before getInitialView when app starts and data can be injected to the spp.

    So for BAG I initialise it in ServiceDelegate.initialise() and for FOR in onBackgroundData or getInitialView

  • The point is that initialize and onStart are called both in background and foreground, and in some cases we need to differentiate at that early point.

  • Simple, don't use onStart :-)

    In app.initialise initialise only this what is necessary for BAG and FOR rest like I've described.