Widget crashes the entire watch

Hi,

I currently have a problem that my widget let crash the entire watch. It stucks at the splash screen. A hard reset helps temporarily. I am wondering how this can happen? Any hints?

  • This should not happen.

    It would be very helpful if you could isolate the problem or provide a test case and information about the environment so that we can reproduce the problem and fix it.

  • I've recently had an app (rather than widget) crashing my watch and stopping on the splash screen … it turned out that in copying and pasting code from a watchface, I had left my main app view extending WatchUi.WatchFace instead of WatchUI.View. Note that it all worked fine in the simulator with it wrong like this Slight smile The crash only occurred when WatchFace.initialize() was called instead of View.initialize(). Although I think it then rebooted the watch automatically and started loading the maps multiple times, then eventually started up again.

  • That's a very good hint . I used inheritance a lot in this widget and have to double check whether the initializes are all set well. Will report back then 

  •  I reported the same crash in this bug report follow up to an issue with settings in the ConnectIQ app.

    forums.garmin.com/.../semicolon-problem-in-pace-settings

    The steps to reproduce have been pretty easy.  I have only tried this on my F5Plus with the ConnectIQ app.  But it has been very reliable to reproduce.  Use ConnectIQ app store to download and install Weather Radar Widget, link below.  Use ConnectIQ app to save an email address in the settings.  It does not need to be a properly registered email address.  Just the act of saving the string will corrupt the settings causing the widget to hard shutdown once run.  Then the next time a CIQ widget is run it hangs on the splash screen.  I attached a videos in the other post.

    When the watch hangs, I can still get into the control menu to power down the watch.  Which is the only way I have been able to clear the problem.  After rebooting I have to uninstall the widget and re-install.  Then if I save the settings using GCM.  The widget will run fine.

    Another note, when the widget shuts down no CIQ log gets generated.

    Please let me know if you have any problems reproducing.  I don't know the exact cause, but know the combination of F5plus beta FW 8.75 and version 1.1.3 of the ConnectIQ app always fail for me.

    apps.garmin.com/.../9c692f27-57d6-4488-91ae-5dd846a4ad26

  • I see the very same behavior on my watch as you described. Especially I can see (as in your video), if the widget crashes the first time, the watchface is shown immediatly. If this happens and you open the widget again, it hangs. The only option you have is to get in the garmin's carusell menu and make a shutdown (or do a hard reset).

    Saying this, I don't believe that it is a setting issue with my widget, since I have isoltated only one setting which is pretty forward, nothing special here.

    I double checked also the initialize statements, but all seems to be correct in my code. I have to track that somehow down, but I don't know currently how.

  • I probably isolated the problem. The crash is gone if comment one line of code. Here comes the code:

    First of all, I have setup a bacground process:

    using Toybox.Application    as App;
    using Toybox.WatchUi        as Ui;
    using Toybox.Background;
    
    
    var batData;
    var myTimer;
    var theme;
    
    (:background)
    class BatteryInfoApp extends App.AppBase {
    
        function initialize() {
            AppBase.initialize();    	
        }
        
        function getInitialView() {
        	if(Toybox.System has :ServiceDelegate) {
        		Background.registerForTemporalEvent(new Time.Duration(300));
        	}
        	batData = new batteryData();
            return [ new BatteryInfoView(), new batteryInfoDelegate()];
        }
    
        function onBackgroundData(data) {
            batData.checkBgData(data);
        }    
    
        function getServiceDelegate(){
            return [new BgbgServiceDelegate()];
        }
    
    
    }

    The background process does the following:

    using Toybox.Background;
    using Toybox.System as Sys;
    using Toybox.Time           as Date;
    using Toybox.Time.Gregorian as Greg;
    
    
    
    (:background)
    class BgbgServiceDelegate extends Toybox.System.ServiceDelegate {
    	
    	function initialize() {
    		Sys.ServiceDelegate.initialize();
    	}
    	
    	
        function onTemporalEvent() {
           	var data = Background.getBackgroundData();
    		if (data == null) {
    			data = {};
    		}
            var stats = Sys.getSystemStats();
           	if (stats.charging) {
            	if (data == null) { data = {}; }
           		data.put("batAtUnplug", stats.battery);
           		data.put("timeAtUnplug", getTimestamp());
      			data.put("isCharging", true);
           	} else {
           	}
            //Sys.println("on TemporalEvent: " + data["batAtUnplug"]);
            
            
            Background.exit(data);
        }
        
        function getTimestamp() {
        	return Date.now().value();
        }
    
    }
    

    Within the main app file above this method is called:

    	function checkBgData(data) {
     		if (data != null) {
     			if (data["batAtUnplug"] != null && data["timeAtUnplug"] != null) {
     				checkUnplugBat(data);
     			}
     		}
    	}
    

    if I comment the line in the main app to call the checkBgData function, the app doesn't crash. Note that I haven't the watch charging during my test, which means the background process comes always back with an empty dictionary..

    I can currently tell you those facts:summarized:

    1. The problem occurs when the background process comes back with an empty dictionary

    2. I can't test in the simulator that the background process collects data (even if it collects no data and returns an empty dict) if the widget is unloaded and after the data collection is loaded (maybe it has something to do with this, namly that the widget's structures aren't initizalized properly before onBackgroundData is called. 

    If I think about this in 2. this is probably the root cause, since the global variable batData is initialized in the getInitialView to prevent it from beeing initialized in the background service. But the onBackground is probably called before the getInitialView. If this is the case, I am wondering how to pass the data properly....

  • I can confirm that this is the problem. I rewrote the code with this semaphore:

    using Toybox.Application    as App;
    using Toybox.WatchUi        as Ui;
    using Toybox.Background;
    
    
    var batData;
    var myTimer;
    var theme;
    
    (:background)
    class BatteryInfoApp extends App.AppBase {
    
    	var batDataInitialized;
    
        function initialize() {
            AppBase.initialize();    	
    		batDataInitialized = false;
        }
        
        function getInitialView() {
        	if(Toybox.System has :ServiceDelegate) {
        		Background.registerForTemporalEvent(new Time.Duration(300));
        	}
        	batData = new batteryData();
        	batDataInitialized = true;
            return [ new BatteryInfoView(), new batteryInfoDelegate()];
        }
    
        function onBackgroundData(data) {
        	//System.println(batDataInitialized);
        	if (batDataInitialized) {
            	batData.checkBgData(data);
        	}
        }    
    
        function getServiceDelegate(){
            return [new BgbgServiceDelegate()];
        }
    
    
    }

    And the problem with the crashes is gone. Neverthelss, I have another problem, namely to process data from the background. Any hint how to do this, since the gloabl variable batData which does the handling of the background's data isn't initialized in time...

  • I probably have solved the problem by this code:

    using Toybox.Application    as App;
    using Toybox.WatchUi        as Ui;
    using Toybox.Background;
    
    
    var batData;
    var myTimer;
    var theme;
    
    (:background)
    class BatteryInfoApp extends App.AppBase {
    
    	var batDataInitialized;
    
        function initialize() {
            AppBase.initialize();    	
    		batDataInitialized = false;
        }
        
        function getInitialView() {
        	if(Toybox.System has :ServiceDelegate) {
        		Background.registerForTemporalEvent(new Time.Duration(300));
        	}
    		initBatData();
            return [ new BatteryInfoView(), new batteryInfoDelegate()];
        }
    
        function onBackgroundData(data) {
        	//System.println(batDataInitialized);
    		initBatData();
            batData.checkBgData(data);
        } 
    
    	function initBatData() {
        	if (!batDataInitialized) {
        		batData = new batteryData();
        	}
        	batDataInitialized = true;
    	}
    
        function getServiceDelegate(){
            return [new BgbgServiceDelegate()];
        }
    
    
    }

  • What was happening looks to be that onBackgroundData() ran before getInitialView() which will happen if the temporal event had been registered in the past. (something is already waiting as soon as the widget starts)

  • Hi Jim, that's what I have figured out as well. Nevertheless, the question is how one can prevent such problems - what do you think about my solution?