Question about (too many) timers and delegate classes

Is there a way to stop a recurring timer that was started in a extended BehaviorDelegate class Initialize function when the class terminates? Reason I'm asking is because I got some ERA "Too Many Timers" crashes. I start three timers, but one in a Glance view and another in a Main view. Since they start in the onShow and stop in the onHide, they shouldn't be active at the same time right?

	function onShow() {
		_refreshTimer = new Timer.Timer();
		if (Properties.getValue("titleScrolling")) {
			_refreshTimer.start(method(:refreshView), 50, true);
		}
		else {
			_refreshTimer.start(method(:refreshView), 500, true);
		}
	}
	
	function onHide() {
		_refreshTimer.stop();
		_refreshTimer = null;
	}

So that leaves the one started in the BehaviorDelegate

 

class MainDelegate extends Ui.BehaviorDelegate {
	:
	var _workTimer;
	:
	function initialize(view as MainView, data, handler) {
		BehaviorDelegate.initialize();
		:
		_workTimer = new Timer.Timer();
		:
		_workTimer.start(method(:workerTimer), 100, true);
		:
	}
	:
}

(the ':' means there are irrelevant code in between the relevant lines)

The watch that had the (many) too many timers errors is a Fenix 5X Plus, so Glance isn't an issue. That MainDelegate class is only created in getInitialView.

I don't understand why it would create too many timers

  • onShow can be called more than once.  create your timers in initialize()

  • Oh, more than once without a corresponding onHide?

    So create in Initialize, start in onShow and stop in onHide but do NOT null the variable there, that's it?

  • I WOULD do set the timer object to null to prevent the many timer issues. I ran into similar things.

    The problem is that if the view isn't active you still have an active timer. So if you create more than 3 you are breaking things. Depending on your workflow and how things are done, loading the same view four times has the ability to break your app with the "Too many timer" errors. If you don't want them, destroy the timer objects ASAP. If you aren't using them: NULL them and just create them when you need them.

    The problem is iirc not the amount of ACTIVE timers, but the TOTAL amount of timers. So doing new Toybox.Timer() 4 times without even starting them will break.::

    As seen here: developer.garmin.com/.../Timer.html

    The number of available timers (default 3)

    I see you use the delegate to control the timer. I would if possible move the timer to your model and control the model from the delegate. Your model can be instantiated once and that means you only have the timer in your model and not possible in the delegate that can be reused among views.

    It is kinda weird that the testsuite is able to run create 65341 Timer objects in a while loop, sooooooo ymmv.

  • Let's say you have a 1 sec timer to update the display.  You can get by with a single timer, no matter how many views you have. 

  • Doing this is really simple.  In your main view (I do it in initialize()) create and start a 1 sec timer.  And in the callback for that timer, do a WatchUi.requestUpdate(),  Then, onUpdate() in the view that's on the top of the view stack will be called every second.  In that onUpdate you can do whatever is needed to update the data in that view.  

    You can also get a bit fancy.  Real example, I have an app where a MapTrackView is used, and at times, I want to display a message for about 15 seconds.  The timer callback is smart enough to know the map is being displayed, and counts down the 15 seconds and only calls requestUpdate if the message is to be displayed.  I never stop the timer

    the OP is also using a 50ms timer.  Things there can be a bit tricky, as depending on what's done each time, not all devices can do what's needed in 50ms. From the API doc: "and the minimum time value (default 50 ms) depends on the host system"

  • The reason for the 50ms timer is to scroll stuff on screen. Anything else and it's not as fluid. That view timer also times "popup" message on screen.

    The delegate timer is responsible for two things. First one is to call makeWebRequest to retrieve data that the main view or subview will display at their own pace. The second use is to go through a command queue that will launch its own makeWebRequest (POST). The queue is filled through button or touchscreen press.

    I again, got a "Too many timers" in my latest version at the

    _refreshTimer.start(method(:refreshView), 50, true);

    line in onShow :-(

    The timer is now initialized in the class Initialize function.
    _refreshTimer = new Timer.Timer();

    From my tests, in Initialize, _refreshTimer was always null so I didn't check for it to be null before creating, Are their occasions that Initialize would be called with _refreshTimer still containing a previous timer?