Multitasking

According to the documentation in Toybox.Ant.GenericChannel.open (API Level 1.0.0):

"Multitasking: Ant channel connection can not be changed while in inacitve mode and ant channels opened during active mode will be closed when app becomes inactive, and re-opened automatically when is active again. These state changes are denoted by calls to AppBase.onActive() and AppBase.onInactive()."

and in Toybox.Application.AppBase.onInactive (API Level 4.2.3, only on some new devices, like fenix8, venu3): 

"Invoked when app enters inactive mode, i.e. hidden by system and not occupying screen. Access to certain system resources will be limited, such as GPS, ANT, Alerts (Vibs tones, flashlight)."

What is multitasking? (I mean I know what it means, but are these devices now have multitasking?) Does this apply to datafields? Or only to watch-apps?

Do I need to save the state (active vs inactive) in a global variable and check it before I call GenericChannel.open? Or is it enough to check whether it returned true or false?

I'm asking this, because I do see some Unhandled Exceptions on the line where I call open(), however what's interesting (besides, that according to the docs open should at worst return false and not throw an exception) is that half of these errors are from older devices, that don't even have onInactive().

  • What is multitasking? (I mean I know what it means, but are these devices now have multitasking?)

    I think multitasking means that the device has a task switcher (recent apps list) [*]. For example FR255/265/955/965 do not have a task switcher, but Fenix 7 does.

    I think this also relates to the behaviour people are complaining about on Fenix 7 where a device app is always terminated when returning to the watch face, even if it's recording an activity.

    (On my FR955, a device app is only terminated when returning to the watch face if is not recording an activity. A device app that's recording an activity will not be terminated.)

    [*] It's implied here:

    https://developer.garmin.com/connect-iq/core-topics/application-and-system-modules/

    Active, Inactive, and Suspended

    Since API level 4.2.0

    Some devices have a task switcher that makes it easy to switch between activities and apps on the device. This can switch your app from active to inactive. To take full advantage of the task switcher, you need to utilize the full app lifecycle.

    It's worth noting that this entire page was once a Connect IQ announcement in a slightly different form, which included a heading entitled Multitasking, even though the core topics page does not mention the word multitasking even once. To me this is even further evidence that "multitasking" implies "task switcher" and vice versa.

    In other words, on devices which don't have a task switcher, you won't see all of the states in the app lifecycle. For example, I will *guess* that devices which don't have a task switcher can never change a running app to inactive or suspended. My guess is that an app is either running or not running on those devices.

    https://forums.garmin.com/developer/connect-iq/b/news-announcements/posts/venu-3-now-available-on-connect-iq-sdk-manager

    This goes back to complaints about Garmin's often confusing usage of terminology.

    Like they expect us to know what "multitasking" is, but they don't really define it anywhere in the official docs. As with a lot of things in CIQ, you have to piece it together from the available evidence.

    I can't answer your other questions about GenericChannel.open except to guess:

    - that the intention of the documentation is to suggest that open would return false if the state is inactive (otherwise there would be a documented exception)

    - that it wouldn't hurt to catch exceptions and treat them the same way as if open returns false.

  • Fenix 7 does

    but fenix 7 doesn't support onInactive

    Regarding error "handling": I didn't handle it until now, and I'm not sure what's the best way to handle it. I (developer) won't be able to see the errors (not more than currently in ERA). I can log it, but if 200k users in 2 years haven't contacted me about errors (even though I see a few in ERA) then even logging the exception's message won't help a lot. Similarly displaying some error code to the user won't be very useful. I won't know about them (even less than now, because the successfully caught exceptions won't be in ERA any more), the user won't understand it, and maybe won't even see it.

  • but fenix 7 doesn't support onInactive

     Interesting, my mistake. Sorry!

    Then I would guess "multitasking" implies "task switcher" but not necessarily the other way around?

    I still think the presence of the "recent apps" switcher in Fenix 7 (and not FR955/965) is somehow related to the fact that, for Fenix 7, CIQ device apps are always terminated when returning to the watch face, even if they are recording an activity. But that's just a guess.

  • Regarding error "handling": I didn't handle it until now, and I'm not sure what's the best way to handle it. I (developer) won't be able to see the errors (not more than currently in ERA). I can log it, but if 200k users in 2 years haven't contacted me about errors (even though I see a few in ERA) then even logging the exception's message won't help a lot. Similarly displaying some error code to the user won't be very useful. I won't know about them (even less than now, because the successfully caught exceptions won't be in ERA any more), the user won't understand it, and maybe won't even see it.

    It all depends on whether you think that when GenericChannel throws an exception or returns false, those are "expected errors" which should be handled gracefully or whether it's appropriate for the app to crash in these cases. I guess it also depends on whether creating that channel is a core part of your app's functionality, or whether the app is still useful if the channel creation fails.

    There may be nothing your app can do to recover if it can't open a channel, but I think most users would prefer to see a friendly error message as opposed to a crash, as a crash suggests to them that the app is malfunctioning.

    Then again, if a user sees an error message and they can't do anything to "fix" it, they may also be frustrated. But at least there's a chance to explain what went wrong.

    e.g. "Unable to open ANT+ channel"

    This is almost like the issue where a CIQ data field will just crash if it tries to create a FIT field but too many FIT fields have already been created (including FIT fields created by other CIQ data field(s)). In this case, many devs expressed that they would prefer that an exception was thrown so they could catch it, as opposed to the app unconditionally terminating due to a fatal error. Sure, I guess in most cases writing a FIT field is just optional part of an app's functionality, but I don't think that changes the general sentiment.

  • maybe it's a documentation bug, that they forgot to add some devices to the list of devices?

  • Good thoughts. My pain is that while I (as a developer) can understand why the too many fit fields cause a crash, and if it was a catchable exception I (as a dev) would know what to recommend to the user (which recommendation might now be buried somewhere in a long FAQ, that most users probably don't reach).

    But here with the GenericChannel it's not even clear to me, so even though it seems like I am able to catch at least some of the exceptions, I don't really know why those happen (and not being able to see even the exception's message, as I am not able to reproduce these errors, I probably won't ever have a clue) so I can't really have any useful tip to the user. So the best thing I can do is to add logs, and wait for another few years, 'till one of the users might contact me and I can ask them to create the txt file and then send me the log file. But that doesn't seem to be worth the effort, especially, with a datafield that is already tight with the memory. Adding meaningful (even to me) logs to the production uses precious bytes.

    Of course in ANT+ HRM there's totally no point continuing without being able to connect to the sensor, but you are maybe right, that even a vague "E001" might be "better" user experience than a crash. On the other hand I am weighing what is better to me: to see these exceptions in ERA (pro: I know they exist, con: there's not much I can do about them, thus they're kind of garbage in ERA) or not to see them (pro: concentrate on the other errors I might be able to fix)

  • maybe it's a documentation bug, that they forgot to add some devices to the list of devices?

    idk, if you search ConnectIQ\Devices\*.api.debug.xml for symbol="onInactive", you get a list of devices which matches the docs.

    So if the docs are wrong, so is the debug info. (I think a lot of this stuff is genned from the same sources, fwiw, especially the type-check info.)

    My current guess is that Fenix 7 does have a task switcher (I mean this part is indisputable [*]), and it does affect the conditions for termination of CIQ apps, but it doesn't support the fancy new app lifecycle in Connect IQ.

    [*] the "recent apps" feature is right in the manual, and users talk about it all the time. It's also clearly not present in FR955 (nor any other pre-FR570/970 Forerunner, afaik)

  • Of course in ANT+ HRM there's totally no point continuing without being able to connect to the sensor, but you are maybe right, that even a vague "E001" might be "better" user experience than a crash.

    At least it's better for PR purposes.

    From the user's POV, an outright crash might look like a bug in your app, whereas you can explain that an app error message is "Garmin's fault". (Yes, we do that for crashes all the time too. And I'm aware users also hate error messages even if we insist it's not our app's fault.)

    Another thing you could do is to continue to try to reconnect every so often, which ofc would be impossible if your app had simply crashed. Ofc you'd have to display some kind of status to the user, which might tough in the limited space available to a data field. (I guess it helps that we have toast notifications now - although those might be annoying unless used sparingly.)

    A somewhat - but not quite - analogous situation is how Stryd Zones recovers from an unexpectedly closed ANT connection. Instead of simply trying to reopen it (which apparently doesn't work - or at least it didn't work 5 years ago on Garmin devices of that era), it actually destroys the GenericChannel object and creates a new one.

    I incorporated that trick into my own app which talks to Stryd.