Screen is cleared in onUpdate ON SOME DEVICES, although onLayout is empty and I don't call View.onUpdate()

Hi, on some devices (like a user's fenix 8 - 47mm, Solar  and my own Edge 840 (non solar), the screen is cleared at every onUpdate, although the onLayour function is empty and I don't call View.onUpdate in my own onUpdate function. I do that so I can continue drawing a graph where I left off in the previous onUpdate and when the whole graph is done, I set a variable (mNoChange) to true. As long as this variable is set to true, onUpdate simply exit. It works well on my Fenix 7S Pro but on my Edge 840, the screen is ALWAYS cleared when onUpdate is called. The only time I clear the screen myself (dc.clear) is when noChange is false (which happens if I press a button or touch the screen to interact with the graph) so I can redraw the graph and I right next after, I write a text (battery level). That text isn't shown so it's NOT because it runs that dc.clear() function. Touching screen do redraw the graph but it's cleared again on the next onUpdate when mNoChange is true. The odd thing is this happens only on my real device, I can't duplicate in the simulator, even if I use the same data to draw the graph on both my device and in the simulator. Is that a known issue for some devices? If so, is there a way around it? It kinda put a wrench in my code to prevent a crash caused by the graph taking too long to draw.

Thanks.

Edit: Well, just saw some posts from a few years ago that "On some devices the screen gets cleared before onUpdate is called". There goes my idea Rage And here I though I was being clever Slight frownSo back to square one, how to prevent onUpdate from crashing by taking too long while drawing something like this!

There are 2498 data points being plotted, although the data points are averaged to drop the drawing to 434 pixel wide (width of the drawing area) as there is no reason to draw over and over the same pixels. Even doing that, and other optimization, it would crash with a CODE EXECUTED FOR TOO LONG. My solution was to stop after 900 ms, save where we were and request a new view update. On the next update, it would continue where it left of and then leave the screen alone when finished until the user interacted with it, new data happened or the battery level changed. It worked so well on my Fenix 7S Pro Cry

  • This comes up all the time, but even if you could be sure that the screen isn't cleared before onUpdate(), it still wouldn't be a good idea to not redraw the entire screen in onUpdate().

    This is because the user could've received a notification that covered all or part of your screen, which means you would have to redraw all or part of the screen anyway. Otherwise, the notification would stay on the screen (or maybe it would be replaced by the background colour, idk). And you have no way of knowing if and when that happens.

    It's a moot point as you know that the screen is cleared before onUpdate() on some real devices, so you have yet another reason to redraw your entire view in onUpdate().

    If you are worried about the cost of rendering your view, maybe you could render it to a BufferedBitmap and draw that in onUpdate()?

    There are a few bug reports and other threads where this has been discussed in the past.

    https://forums.garmin.com/developer/connect-iq/i/bug-reports/bug-some-newer-devices-clear-the-screen-on-every-view-update-simulator-behaves-differently

    Here's a bug report with a salient comment from a CIQ team member:

    https://forums.garmin.com/developer/connect-iq/i/bug-reports/vivoactive-4-clears-screen-before-onupdate 

    what you are seeing is a normal thing and is currently expected. We have a few different frameworks that are used to do wire management. One clears the screen first and one does not.

    You should just do what is the most compatible.. make no assumptions and clear the screen yourself when onUpdate is called.

    "make no assumptions" - I think this is great advice when it comes to software development in general. It's too bad the CIQ simulator behaves wildly different than the real devices in so many cases tho. The CIQ development experience is still very frustrating and it's already been 10 years.

    https://forums.garmin.com/developer/connect-iq/f/discussion/383881/persistence-of-the-dc-content-between-calls-to-onupdate

  • Ok, I'll look at BufferedBitmap, I'm assuming this is decoupled from the onUpdate code (ie, through a timer maybe?) and the onUpdate simply redraws the latest finished bitmap? Assuming there are two bitmap being used here. One currently being displayed (or redisplayed if for example a notification covered part of the screen) and the other one is the one being worked on?

  • There's only one buffered bitmap at play. It's a canvas that you draw on, and when you need to update the real screen, a single API call will copy the buffered bitmap to the screen (just like drawing a regular bitmap)

    You draw the relevant part of your view on the BufferedBitmap whenever the view state changes. If this is a data field where the system schedules regular calls to onUpdate once per second, it would make sense to do it here. Same goes for watchfaces. 

    On every call to onUpdate(), whether or not you updated the buffered bitmap, you simply it draw on the real Dc, with a single dc.draw call.

    Since you said that you don't like re-rendering the entire graph/screen on each call to onUpdate(), this method would allow you to only render the parts that change. The parts that didn't change obviously remain unchanged in the buffered bitmap.

    Of course there's still the cost of dc.draw call that draws the buffered bitmap to the screen, but I don't think there's any getting around that. 

    You might want to read about buffered bitmaps here, especially the part about how they interact with the graphics pool:

    https://developer.garmin.com/connect-iq/core-topics/graphics/ 

  • Thanks. It's an app so is there a better time than a timer to 'draw' the bitmap.

    Regarding a single buffer, what happens if I get an onUpdate while I'm mid way drawing the graph? For example, if the user "zooms in", I need to redraw the whole thing and it might take two passes for the graph to be completed. Hence why I thought about using a complete bitmap that I display and one that I'm working on until the one being worked on is done, then I display that one and the the one that was on display before can be cleared and worked upon. Rinse and repeat.

  • If you are in the middle of an onUpdate call. it won't get interrupted.  The first onUpdate completes before onUpdate is called again

  • Hi, that I know. it's more like if I use a timer to draw the graph that the onUpdate will display and it takes two timer events to fully render the graph. What happens if a onUpdate happens in between both timer events is my concern. Hence having two buffers. One actively being drawn into and the other being displayed.

  • I see now. In that case it def makes a ton of sense to use 2 buffers.

    Sorry, I should've read the OP more carefully haha.

  • Ok, glad to see I can use two buffera Slight smile

  • Thanks. It's now working. I have a 100 ms timer who's job is the render the graph when its view is about to be shown. It might require more than one timer event (hence the short period) before a graph is fully rendered. Once fully rendered, I call requestUpdate so it can be shown as soon as possible. I use two class variable in my View Class, mOffScreenBuffer and mOnScreenBuffer. Once I've fully rendered the graph using mOffScreenBuffer, I copy it into mOnScreenBuffer and clear mOffScreenBuffer so it's ready to redraw in a timer event when something changes. In onUpdate, I show a 'Please wait' popup if mOnScreenBuffer is null (which I set it to null when leaving the view) and its 'bitmap' when it's not null. Thanks again for your help.

  • Nice! I have a question about this:

    Once I've fully rendered the graph using mOffScreenBuffer, I copy it into mOnScreenBuffer and clear mOffScreenBuffer so it's ready to redraw in a timer event when something changes.

    Do you literally copy the contents of mOffScreenBuffer into mOnScreenBuffer? Because that seems unnecessarily expensive. The time for the copy step will be O(n), where n is the number of pixels in the buffer. In other words, the time will be proportional to the number of pixels.

    I would suggest simply exchanging the roles of the existing buffers instead by swapping references (which takes a constant amount of time). Instead of copying the offscreen buffer to the onscreen buffer, now you just need to create a new offscreen buffer

    e.g. 

    // 1) finish drawing full graph using mOffScreenBuffer
    // ...
    //

    // 2) assign offscreen buffer reference to the onscreen buffer variable (constant time)
    // (iow, the offscreen buffer is now the onscreen buffer)
    mOnScreenBuffer = mOffScreenBuffer;

    // 3) create new offscreen buffer
    mOffScreenBuffer = /* create new buffered bitmap */; 

    If 3) takes the same (or similar) amount of time as clearing a buffer, then your net savings is indeed the same as the amount of time it would take to copy a whole buffer.