Overusing Storage.setValue()?

Some of you might have got ERA reports with crashes when using Storage.setValue(). I have had them a lot. Although I think the original problem is on Garmins end I'm thinking I might be overusing Storage.setValue().

I have a timer that updates every second. And at the end of the loop, I store the value in storage to be able to access it from the menu etc. Since everything has worked before, i didn't think much about it, but is this a bit of overuse of the setValue()? I thought it should handle write every second, but I could of course reduce the number of writes by writing whenever I go to the menu or any other page apart from my main page.

This would not remove all calls to setValue() but would certainly reduce my exposure to the risk. Am I not using setValue as intended?

  • TL;DR consider using a global variable instead of storage, if possible.

    Accessing storage is expensive (e.g. slow, battery-intensive), so it probably isn't a good idea to write it every second. Even in the absence of a Garmin bug/crash, this practice seems like it would waste battery life and possibly noticeably slow the app down (i.e. perhaps the UI might seem more sluggish then it should be.)

    Why do you need to use Storage.setValue() in the first place?

    By "the menu or any other page apart from my main page" I assume you're referring to views within the same app. While the best approach would be to directly pass whatever data these views need in their constructors (obviously these would have to be custom view classes that you implement), if that's too much of a redesign, you could always use global variables (perhaps design one class that has all your global state, and create one instance of that class as a global variable.)

    Storage should only be necessary if you need to persist data between multiple runs of your app. But even then, you wouldn't want to be constantly reading or writing storage.

  • I'd avoid using Storage every second.  It's expensive for both time and battery as the file system is involved.  Try saving it once in onStop()

    And I sure wouldn't use Storage to pass info to things like a menu.  I'd pass the view to the delegate and have it just access things available to the view.  The easier but less efficient way would be to make the info you need to be in globals, but be really careful if you have a background service.

  • I get those ERA reports a lot too, and I don't even use Storage unless I have to to persist values. Certainly no where near as often as once per second.  Just when an important event or change happens that I need to remember.  I would certainly like to know what is causing this.

  • Thank you for your input, FlowState and jim. I actually thought that storage was a "cleaner" way than using globals. I have no trouble using globals instead. That could be done quite easily.

    I am referring to other views and menus within my app. And there are views that need to keep track of time and score and such. I'll make a fast switch to globals whereever I can (still need to use some storage for keeping variables between app sessions) and see if I can redesign my code for the long run.

  • While appbase.setProperty/getProperty has been deprecated for a while it works still on new devices. Because setProperty does not write it's values to disk immediately - I think that only at application exit it writes it's values back to disk - this is an awesome means to have as an application cache. I hope it never gets discontinued.

    And sure the storage module is much more sturdy as it is saving the value immediately it will also have it's data stored in case of an unexpected application exit, but in most cases I prefer speed of operation over the certainty that a value has been stored and saved.

    If you do not wish to use deprecated functionality, you can use class variables or if you really must global variables as flowstate suggests.

  • While appbase.setProperty/getProperty has been deprecated for a while it works still on new devices. Because setProperty does not write it's values to disk immediately - I think that only at application exit it writes it's values back to disk - this is an awesome means to have as an application cache. I hope it never gets discontinued.

    If you're not tight on memory, you could always reimplement this functionality with an in-memory cache that only gets saved to Storage in AppBase.onStop(). Assuming that setProperty() doesn't write to "disk" in the case of an app crash (which seems to be what you implied), then your replacement functionality could work identically to get/setProperty().

  • While appbase.setProperty/getProperty has been deprecated for a while it works still on new devices.
    If you do not wish to use deprecated functionality

    Probably worth mentioning that a Garmin employee has publicly stated that Garmin has wanted to remove this functionality forever, but they can't do so because CIQ devs won't stop using it, and that fact is frustrating for them.

    Ofc that could be used as an argument either for or against using the deprecated functionality.

    But Garmin absolutely intended on removing it, they just haven't done it bc they don't want to break existing apps. Who knows if they'll put their foot down eventually.

  • I filed a bug report on this about a year and a half ago, 23907951K1. Basically, If I updated storage too frequently it hard rebooted my Fenix 7X. When I reported that if I updated storage less frequently the problem happened less, their response was "you fixed it, so we won't". They are not going to fix it. Your only option is to figure out how to mitigate it.

  • In the aftermath of all "globalization" of variables previously in Storage just to cope with the crashes, I'm getting more time to think about a better solution.

    When you say "I'd pass the view to the delegate and have it just access things available to the view". Do you mean that "the view" is my main view (or the view that holds all relevant variables) and that view is passed on to the menu delegate (or other views delegates)?

    maybe I have done a more fundamental mistake. My main view is pretty slim, and it is the main delegate that holds most of the functionality and the variables. I mean, it works, but might not be the best way? Could I pass the main delegate instead?

  • Ok, here's the basics.  I pass the view to the delegate:

        function getInitialView() {
        	view=new WUWView(); 
            return [ view, new WUWDelegate(view) ];
        }
        

    Then in 

    class WUWDelegate extends Ui.BehaviorDelegate {
    	var view;
        function initialize(v) {
            BehaviorDelegate.initialize();
            view=v;
        }
    I have the view, and with something like onMenu, I pass the view to that delegate
        function onMenu() {
        	handleStation();
        	Ui.requestUpdate();
        	return true;
        }
        
    	function handleStation() {
       		var myMenu=new Ui.Menu();
       		myMenu.setTitle("Change Station");
        	myMenu.addItem("Get Current Location",:item_GPS);
        	if(MySettings.hstation!=null) {myMenu.addItem("Use Home: "+MySettings.hstation,:item_HOME);}
        	if(MySettings.station!=null) {myMenu.addItem("Save Current as Home",:item_SAVE);}
        	Ui.pushView(myMenu, new WUGwidMenuDelegate(view), Ui.SLIDE_UP);			
    	} 
    Then, in the menu delegate,
    class WUGwidMenuDelegate extends Ui.MenuInputDelegate {
    	var view;
    	
        function initialize(v) {
            MenuInputDelegate.initialize();
            view=v;
        }
        
        ....
    So the view is passed to the Behavior delegate, which passes the view to the Menu delegate, where you can then use something like
    view.forecastRequested=false;
    to change a variable in the view, that can impact what's shown with onUpdate.
    You will see something like this in some of the samples in the SDK.