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.
  • Hey,

    I've got a bug report captured.

    Thanks,
    -Coleman
  • Hi Coleman.ConnectIQ ,

    Can you please put some pressure on the team / higher priority on the bug to be looked at?
    Especially the double call of onUpdate.

    It has serious rammifications when making a web request from within the onUpdate, causing the request to be sent twice in quick succession, and this affects any api call credits negatively.
    I had to build in a gate to check if there is an ongoing request, and this makes the code sloppy. It really should be handled better by the framework and not put the responsibility on the developer to build in hacks.
    And this after some hours of troubleshooting / debugging. :(

    Thanks.

    H
  • It really isn't a good idea to be triggering application logic off of display update calls. What you are seeing in this instance really shouldn't be happening, and might get fixed, but is almost certainly going to be scenarios on a device where you could receive extra calls to onUpdate(). The user might receive a notification that appears over your application, which would require redrawing your view. Rendering your application is something you should always assume you have no control over.
  • Hi Brian,

    Thanks for the response.

    I do agree that there is a case to be made for a model-view-controller pattern, but I haven’t really seen anything of that in the samples, unless I’ve overlooked it by mistake.

    All my apps are exclusively watch faces, and given the limited memory allocated to watch faces, I’ve always tried to steer away from extra classes and “fancy” implementations in order to keep the code as lean as possible. As a result, almost all of the application logic reside in the views.

    I am open for suggestions of how best to implement some kind of view-controller pattern in a CIQ app.

    I do understand that the onUpdate might be unreliable, but the fact is, there still is obviously incorrect behaviour being observed, which should be addressed and corrected despite the coding patterns applied by a developer, be they good or bad.
  • I don't disagree that this behavior is obviously wrong and should be corrected. I just wanted to point out that there could be perfectly normal behavior that would trigger extra calls to onUpdate() or other rendering methods.

    I don't think you need to make your code massively more complex to get logic like this out of your views. If you have a web request that needs to be made once when a view is created, you should initiate that request along with the control logic that pushed that view rather than the display logic of the view. If you're pushing your memory limits and doing something like this in onUpdate is really the only option you have, then I think you would need to accept that your solution also requires some sort of "one time" flag since you can't make assumptions about how many times the system will need to render your view.
  • Thanks Brian,

    I understand what you’re saying.
    I appreciate your ideas and suggestions and will definitely explore it a bit further.

    Currently I am working on a widget, so I should have more available memory to push the envelope a bit.

    Great talking to you!
  • This is one of the basic UI logic how the page transitions are handled on Garmin devices, there always be more than one onUpdate() call during a view transition push/pop/switch. When a view is pushed onUpdate() is invoked in current view, and another onUpdate() on new view, both the UI are buffered up and a slide_* animation is performed and then onUpdate() is invoked on the new view this has been like this since forever, Different devices have different implementation, some invoke onUpdate() per frame during the transition on both the new and old view.
  • Ok, the per-frame for the animation part makes sense.
    thanks for explaining that.

    Now I’m curious to know if the slide immediate will eliminate the multiple calls of the same onUpdate.

    Please don’t forget to take a look at issue 3 as per my original post. I don’t see a logical explanation for that one since it happens indefinitely and not linked to animation frames.
  • onUpdate() triggering multiple times has been reported before. https://forums.garmin.com/forum/developers/connect-iq/1437584-bug-in-connect-iq-3-0-6-sdk-but-same-in-3-07

    I have a text-based watch face where I would change the time at random picking from variants like "a bit past five" or "little past five". Every minute, the watch face would rapidly change between variants for ~ 10 to 20 times. I thought about moving the variant-picking-logic out of onUpdate() but creating my own timer logic to be triggered every 60 seconds seemed a bit excessive workaround for something that looks like a clear bug. I removed the randomness.

    If you're building a watch face and want something() to happen every minute (but only once a minute), what is the correct way of invoking something()?

    HermoT, please let me know if slide immediate helps. I was not doing view transitions (push/pop/switch).

    You can check example code at https://github.com/samuelmr/garmin-updatecount
    (The project in github uses custom font that's not available in the repository.)
  • EDIT: Sorry, this is not best solution for the problem as stated above. Please disregard this post and refer to Jim's solution below


    samuelmr this is just a thought, but could you just rate-limit whatever you want to do with System.getTimer() (which basically returns the uptime in milliseconds)?

    e.g.

    function onUpdate() {
    doSomethingEveryMinute();

    // do other stuff...
    }

    var lastUpdateTime = null;
    function doSomethingEveryMinute() {
    // System.getTimer() returns a signed 32-bit integer, but it's okay because we only
    // need look at the difference between two values, and the arithmetic works
    // even when the value wraps (at most once) from positive to negative
    var currentUpdateTime = System.getTimer();

    if (lastUpdateTime == null || currentUpdateTime - lastUpdateTime >= 60000) {
    something();
    }

    lastUpdateTime = currentUpdateTime;
    }

    function something() {
    // ...
    }