Oddities observed in log on device regarding calls to onUpdate and draw

While developing a widget, I discovered 2 cases where the CIQ framework incorrectly calls certain methods more than once, as well as calling methods out of the order of logical sequence:
  • On the main view, when you return back to it from a menu or another view on the view stack, the main view's onUpdate is called twice. (This seem to happen on both the actual device as well as the sim)
  • If you press the menu button on the home view (which causes a menu to be pushed onto the stack), the home view onUpdate() gets called again. Why is this necessary?
  • On a menu with a drawable title, the drawable title's draw() method is called indefinitely at a rate quicker than 1 second. (This only happens on the actual device)
I then modified the Menu2Sample project (that ships with the SDK), to illustrate the issue.
I added print statements in the methods that get called by the framework.

I have attached both the modified project as well as log files pulled off a f5+ to illustrate the issue, but here is a quick screenshot to show the issue:



To reproduce:
  • Build the project for a device, side load to the device
  • Enable logging on device by adding txt file to LOGS folder
  • Open the widget on the device. This triggers the home view's onUpdate() method twice.
  • Press the menu button and wait for about 3 seconds. Later in the log you will note that the main menu's drawable title's draw() method was called >-3 times. The longer you wait, the more log entries you will see for this.
  • Press back button to return to the home view. This will call onUpdate() of the home view twice again.
The impact of these:
draw() method being called indefinitely at a rate faster than 1 second is a concern for battery
Multiple calls of onUpdate could also lead to drawing issues for unexpecting developers, and although less significant than the draw(), it could also negatively impact the battery.
  • Not related to Hermo's initial post, but -
    if you want something to change when the minute changes, the easiest way is to track the minute, consider the h:m:s is something you already need.

    var lastMin=-1; //always happens the first time onUpdate is called

    function onUpdate(dc) {
    var now=Sys.getClockTime(); //or Time.now(); etc
    var min=now.min;
    if(min!=lastMin) {
    //it's a new minute, so do what needs to change here
    lastMin=min;
    }
    //the display of the watch face
    }


    I do a similar thing where I have data fields that can rotate through a few different option every minute. Timers can only be used in watch faces when they are not in low power mode, and using Sys.getTimer() can be avoided, as a WF will already have the minute (it is displaying the time after all)..

    This will handle whatever happens. It could be that onUpdate() is called multiple times at the top of the minute, the WF is in high power mode when it passes the top of the minute (onUpdate() called every second), onUpdate() is called after something like a notification occurs, or if Ui.requestUpdate() is called after something like receiving background data.
  • EDIT, Sorry I misread the problem statement. The problem was to do something when the time changes, and this post really only applies to generic scheduling -- i.e. do something every 60 seconds, but no necessarily at the top of the minute -- (and really only when onUpdate() is called every second, as in a data field).

    And, as Jim pointed out, for a watchface the time can't change without exiting it, so that concern is moot as well.

    So please disregard the remainder of this post...


    I don't want to be super-pedantic, but doing something every 60 seconds, and doing something when the minute portion of the "wall clock" time changes are two similar things, but not identical. I'll just point out that the solution with System.getTimer():

    • Can be easily adapted to any interval/situation you want. e.g. Imagine a situation where you have a data field and you want to do something every 15 seconds (without the use of a timer). Imagine you implement onPartialUpdate() in your watchface and you want to do something every 15 seconds.
    • Won't behave in unexpected ways if the clock time changes (e.g. time is set manually by user, or time is adjusted by GPS sync). I realize this is very rare, but IMO, it's good to consider corner cases like the clock time changing, because:
      • There may be other cases where it's very important.
      • It's good to get in the habit of considering corner cases, because quite often even very experienced coders brush off corner cases when they are important, which leads to very predictable bugs down the line
      • There is actually one significant case for Garmins where the watch can lose significant amounts of time (several minutes) -- see below

    In general, I just prefer using the system uptime in almost every case where I want to schedule something based on a relative interval, as opposed to using the clock time. Because the clock time can always change in ways you don't expect.

    I also prefer to use solutions that can be adapted to different situations (provided there isn't too much overhead.)

    Of course, in this specific case the choice of solutions is just down to personal preference.

    But here's an example of a case where using the clock time for this kind of thing can go wrong:
    1) App schedules some sort of one-time alarm or event that goes off in about five minutes (without the use of a timer). The event time is defined as "five minutes from now" and not "the current time -- e.g. 8:00 -- plus 5 minutes" (note the subtle distinction)
    2) The user sets the clock forward more than five minutes. Maybe the clock was corrected by GPS sync. (If you look in the forums, many ppl have complained about their Garmin "losing time" in the past, due to a unexpected crash/power failure, as Garmins don't have a real-time clock that keeps going when the watch loses power or reboots.)
    3) Unexpectedly, the app event never happens

    I only bring this up because at my day job I've seen this kind of scheduling issue (*) come up more than once because very experienced coders assumed that the clock time could never "jump" backwards or forwards unexpectedly, as opposed to flowing continuously. If you want a clock that doesn't have those issues, the system uptime is usually your best bet.

    (*) And every time management agreed it was a bug, and it was something that had to be fixed. But it was the kind of thing that could've been predicted immediately, as soon as you saw someone use the wall clock to schedule an event with a relative interval.

    ---

    A different way of looking at it: if you used a CIQ timer to implement your once-per-minute event, you would expect the timer to work no matter what happened with the clock -- the user should be able to change the clock as much as they want, and the timer should keep firing every minute. Well, wouldn't you want your own solution to work the same way? If that's the case, System.getTimer() is the way to go.

  • Again, the question was about a watch face, where the WF itself generally doesn't have any control over when onUpdate() is called, but when it's called is a bit regulated. They are different than things like widgets and device-apps (the exception is something like having a background process with onBackgroundData or after something like onSettingsChanged when the app may want to force a call). Other than that, it's consistent that onUpdate() is called at the top of every minute, or every second (for 10 seconds) after a gesture (and you can tell the difference with onExitSleep() and onEnterSleep().

    Let's say there's a gesture or a notification is cleared 30 seconds into a minute and your getSystemTimer() code says to change. After that, the change will be at 30 seconds after a minute until the next gesture/notification, so when looking at the time, it will change 30 seconds late. Using the minute, the time change when the minute actually changes (a good thing for a WF to do)

    But let's say it is a widget or device app where you want to update the screen every minute. A common trick there is when the app starts, start a one-shot timer for the time until the top of the next minute, and when that fires, a repeating timer for 60 seconds. Again, for a case where you want to displayed time to change when the actual time changes. For more precision, you could restart the timer each time it fires and set it to the top of the next minute.

    If a user changes the time manually or by starting GPS, that also means the user has exited the WF to do so, BTW.
  • Reposting my reply which was eaten by the forum upgrade:

    ---

    @jim_m_58, makes sense, thanks! I misread the problem statement and didn't realize it was specifically about doing something at the top of the minute. I thought it was about changing the time format every 60 seconds (which in hindsight, doesn't really make sense). So yeah, my solution doesn't really make sense. System.getTimer() would be an okay approach for generic scheduling if you have a datafield where onUpdate() is called every second, but not for this situation.

    Sorry for wasting your time @samuelmr.

  • Actually, I was changing the time format every minute. (E.g. showing "a bit past five" sometimes and "little past seven" other times.) It might make sense after all.

    Anyways, Jim's workaround makes sense. Thanks for the help, guys!

  • Hmm, that’s what I thought. Sorry, I wasn’t criticizing the concept of your watch face or anything — I was just convinced that I didn’t understand the problem statement based on some of the replies which referred to “top of the minute” and “updating the time”, which I feel is not quite the same as doing something once every sixty seconds, especially if the frequency could be changed. Although I do see the advantage (and, in some cases, necessity) of doing things at the top of the minute. And I guess it probably wouldn’t work to change the format more frequently than once per minute anyway, due to power budget constraints.

    Have fun developing your watchface!