Appropriate way to handling exiting/onBack in an app?

Former Member
Former Member
I apologize if this has been asked before - I didn't find anything on searching.

What is the appropriate implementation for app developers to ensure proper exiting/backing out of a watch app? Currently, when I load my app onto my Fenix 3 it works well but when I press the "Back" button to exit, it crashes. The code for my main BehaviorDelegate is below.

Also - a previous version of this code was not implementing onBack, and still crashed. Any help is appreciated - thanks.

(On a related note - what is the appropriate handling for the Up/Down keys in a widget to ensure that one can cycle to the adjacent widgets? Or should they be left un-handled?)


class LoopTimerDelegate extends Ui.BehaviorDelegate
{
hidden var app;

function initialize()
{
app = App.getApp();
}

function onMenu()
{
Ui.pushView(app.getNewSettingsMenu(), new LoopTimerMenuDelegate(), Ui.SLIDE_UP);

return true;
}

function onBack()
{
// Handle exiting
Ui.popView(Ui.SLIDE_RIGHT);
return true;
}

function onKey(evt)
{
var key = evt.getKey();
var handled = false;

if (WatchUi.KEY_ENTER == key)
{
app.toggleTimer();
handled = true;
}
else if (WatchUi.KEY_DOWN == key)
{
// Reset the timer
app.resetCurrentIteration();
handled = true;
}

if (handled)
{
Ui.requestUpdate();
}

return handled;
}
}
  • I asked this question recently in this post.

    Widgets should not pop their initial view; simply return false when the back/escape key is pressed from the last view. For an application, can either pop the final view yourself, or you can just return false and let the system do it.
  • Former Member
    Former Member over 10 years ago
    The up and down keys will not be passed to the base view of your widget for handling. The back key also should not be passed to your widget. If removing the handling of the back key does not resolve the crash you are seeing, you should check your code in onStop/onHide functions for an error.
  • Former Member
    Former Member over 10 years ago
    Travis, Brian - thank you both for your help (and my apologies for missing that thread earlier).

    My app is still crashing on back button press to exit (on the Fenix 3) - I'm not sure what I'm doing incorrectly.

    Here's what I've tried thus far without success (note again that the app is loaded onto the Fenix 3 as an App, not a widget)

    1) Removed any handling of onBack or WatchUi.KEY_ESC from base View's BehaviorDelegate (per Brian + Travis' suggestions + the earlier thread) -> still crashing when Back button is pressed on Fenix3 to exit the app

    2) Removed the onHide and onShow functions from my base View (these were previously empty so I was doubtful that these were the culprit) -> still crashing on Back button press

    3) Added handling for WatchUi.KEY_ESC to base View's BehaviorDelegate - on KEY_ESC, Ui.popView then Ui.requestUpdate are called, followed by "true" being returned from the onKey method -> still crashing

    I'm not sure what's the correct thing to do at this point - any help is appreciated (and I'm happy to post any and all code). Do any of the current apps on the Connect IQ store for the Fenix 3 have their codebase on Github so I can peruse to see how they handle exiting?

    (Also - on a semi-related note - Brian - I can confirm that the "Down" key is indeed passed to widgets because when I originally built the app as a widget, I was receiving and handling the Down key - and subsequently preventing my widget from ever exiting)

    Thanks.
  • Hi LastBlueJay,

    It sounds like you are seeing the crash on the watch, but just in case you're on the simulator.... In my experience, the simulator crashes when the last view is popped, but the watch (920XT) does not.
  • Former Member
    Former Member over 10 years ago
    (Also - on a semi-related note - Brian - I can confirm that the "Down" key is indeed passed to widgets because when I originally built the app as a widget, I was receiving and handling the Down key - and subsequently preventing my widget from ever exiting)


    I went and checked this after reading your post and noticed that it was not being handled properly. So... in future Fenix 3 software versions, these keys will not be provided to the base view of Widgets.
  • Former Member
    Former Member over 10 years ago
    Hi LastBlueJay,

    It sounds like you are seeing the crash on the watch, but just in case you're on the simulator.... In my experience, the simulator crashes when the last view is popped, but the watch (920XT) does not.


    Thanks MoxyRoger. Unfortunately yeah it is on the watch. I will try a few more things and see.
  • Former Member
    Former Member over 10 years ago
    I recommend creating the CIQ_LOG.txt file in the LOGS folder to get the ConnectIQ crash log from the device. That should help point you to the source of the error.
  • Former Member
    Former Member over 10 years ago
    Brian - thanks for the tip - alas, it seems, oddly, that CIQ_LOG.txt is not logged to on the Fenix 3. However, when I created AppName.txt (with my specific app name) - then I would get any print/println statements there (no stack dumps though). So there might be a bug with CIQ_LOG.txt logging on the Fenix 3 (the file had full write permissions btw).

    Nevertheless - I found the issue by just starting from scratch and putting things back in one chunk at a time.

    The code below is what was causing the app to crash on back/exit (specifically the line in bold):

    class LoopTimerDelegate extends Ui.BehaviorDelegate
    {
    hidden var app;

    function initialize()
    {
    app = App.getApp();
    }



    When I removed storing the App.getApp() in an instance variable on the base Delegate, it fixed the crashing. I'm not sure if I was using App.getApp() inappropriately (everything works ok if I store App.getApp() temporarily as a variable in the specific functions that use it). I suppose I was just trying to be minimially more efficient by storing it once on the Delegate.

    It doesn't seem like it should cause the app to crash when exiting though but it does (at least on the Fenix 3). I've also noticed an issue where class instance variables (that have clearly been assigned to) will show up as Null in certain class functions - I'm going to put together a test app to see if I can isolate and replicate the issue - hopefully that will help.

    Thanks for the help.
  • Former Member
    Former Member over 10 years ago
    The CIQ_LOG.txt should definitely be functional on Fenix 3. It is possible something about this particular crash is preventing it from logging. (This is an app crash, not a device crash right?)

    I don't see anything wrong with that code snippet. We will investigate this issue.
  • Former Member
    Former Member over 10 years ago
    The CIQ_LOG.txt should definitely be functional on Fenix 3. It is possible something about this particular crash is preventing it from logging. (This is an app crash, not a device crash right?)

    I don't see anything wrong with that code snippet. We will investigate this issue.


    Hi Brian - I didn't know there was a difference between an app crash vs. device crash but I'm guessing it's a device crash because the whole unit resets (shows the Garmin start-up logo, etc.).

    Below is code for a very simple App that will do two things - crash on exit/back on the Fenix 3 AND if you run it in the simulator you'll notice bizarre behavior when it tries to print "vibrateData" - it shows up as Null even though it is clearly assigned (I thought it might have been an issue with hidden vs. not hidden but there's no difference). This replicates an issue I noticed while coding my app which resulted in having to move variables around so they would show up as not null. I'm not sure if it's related to why the app crashes on the Fenix 3 on exit.

    Bizarrely - the sample app/widget "Attention" works just fine with the vibrateData being assigned in the class definition.

    Hope this helps somewhat.

    using Toybox.Application as App;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class TestAppCrashApp extends App.AppBase {

    hidden var vibrateData = [ new Attention.VibeProfile(75, 1000) ];
    var vibrateData2 = [ new Attention.VibeProfile(75, 1000) ];

    //! onStart() is called on application start up
    function onStart() {
    Sys.println("");
    Sys.println("onStart");
    Sys.println("vibrateData = " + vibrateData);
    Sys.println("vibrateData2 = " + vibrateData2);
    }

    //! onStop() is called when your application is exiting
    function onStop() {
    }

    //! Return the initial view of your application here
    function getInitialView() {
    return [ new TestAppCrashView(), new TestAppCrashDelegate() ];
    }

    }

    class TestAppCrashDelegate extends Ui.BehaviorDelegate {

    hidden var app;
    var app2;

    function initialize() {
    app = App.getApp();
    app2 = App.getApp();

    Sys.println("Delegate::initialize");
    Sys.println("app = " + app);
    Sys.println("app2 = " + app2);
    }

    function onMenu() {
    Ui.pushView(new Rez.Menus.MainMenu(), new TestAppCrashMenuDelegate(), Ui.SLIDE_UP);
    return true;
    }

    }

    class TestAppCrashView extends Ui.View {

    //! Load your resources here
    function onLayout(dc) {
    setLayout(Rez.Layouts.MainLayout(dc));
    }

    //! Restore the state of the app and prepare the view to be shown
    function onShow() {
    }

    //! Update the view
    function onUpdate(dc) {
    // Call the parent onUpdate function to redraw the layout
    View.onUpdate(dc);
    }

    //! Called when this View is removed from the screen. Save the
    //! state of your app here.
    function onHide() {
    }

    }

    class TestAppCrashMenuDelegate extends Ui.MenuInputDelegate {

    function onMenuItem(item) {
    if (item == :item_1) {
    Sys.println("item 1");
    } else if (item == :item_2) {
    Sys.println("item 2");
    }
    }

    }