Circular reference using mailBoxListener

Former Member
Former Member
Hi everyone,
I'd like to raise this issue exlicitly once again, since it's been a while, several GCM, SDK and actual watch firmware versions, but alas nothing changed and my original post was left unanswered, maybe due to specific questions about BLE debug.

The issue is: if I define mailBoxListener as shown in Comm example and application receives data from companion application installed on paired phone, then this watch app exit is delayed for several seconds and "Circular Dependency" entry is added to CIQ log (maybe this is the actual cause of the delay since file IO is very slow). This is not a crash, just an error log entry which is added after application closes. Comm example manipulates global strings, but problem appears even if nothing is actually passed outside of the callback method.

class TestView extends Ui.View {
function initialize() {
View.initialize();
Comm.setMailboxListener( method(:onMail) );
}
...
function onMail(mailIter) {
var logStr = null;
var mailStr = mailIter.next();
while( mailStr != null ) {
logStr = mailStr;
mailStr = mailIter.next();
}
Comm.emptyMailbox();
Ui.requestUpdate();
}
}


This example does even less than callback method in Comm example, but problem still persists.
The type of application doesn't matter: I experienced this in both App and Widget. Scope of the callback doesn't matter: I tried to use Comm example model, when mail listener callback is defined as method( :<This class method> ) (example code is shown above), I also tried to define this callback in AppBase descendant class and transfer it as the parameter to View, which sets up listener using parent method with the same results.

class TestApp extends App.AppBase {
...
function getInitialView() {
return [ new TestView( method(:onMail) ) ];
}
...
function onMail(mailIter) {
var logStr = null;
var mailStr = mailIter.next();
while( mailStr != null ) {
logStr = mailStr;
mailStr = mailIter.next();
}
Comm.emptyMailbox();
Ui.requestUpdate();
}
...
}

class TestView extends Ui.View {
function initialize(callback) {
View.initialize();
Comm.setMailboxListener( callback );
}
...
}


I even tried to move this listener to AppBase descendant, results are the same.

class TestApp extends App.AppBase {
function initialize() {
AppBase.initialize();
Comm.setMailboxListener( method(:onMail) );
}
...
function onMail(mailIter) {
var logStr = null;
var mailStr = mailIter.next();
while( mailStr != null ) {
logStr = mailStr;
mailStr = mailIter.next();
}
Comm.emptyMailbox();
Ui.requestUpdate();
}
}


On my Forerunner230 problem is reproduced even using standard Comm example (of course this requires Android part to be assembled and installed on phone). There is no such issue if application defines listener but doesn't receive actual data.

I lived with this issue in an App, because data was rarely synchronized, but in case of widget, which is supposed to receive frequent updates from the phone, this causes major slowdowns when navigating through widgets, since widget doesn't stay as a background service but is initialized and destroyed each time it is shown on screen. Thus, after data was received, navigating through this widget is slowed considerably. Since none of my efforts resulted in success even using standard Comm example, I consider this an SDK bug and would very like to receive feedback from someone more experienced in Garmin/Android communication development or one of the Garmin devs. Please, explain this behaviour and provide tips on how to avoid this issue.
Thanks in advance.

P.S. This problem becomes worse in case of widget, because I already experienced native apps crashes to watch face and even watch reboots after this "Circular Dependency" issues happened several times in a row (no errors were added to CIQ log). It seems that in case of widget memory management is different and this kind of error may leave some objects to be skipped by garbage colletor leading to unexpected out-of-memory scenarios. Therefore I encourage Garmin developers to research this issue because a lot of people like to use their Garmin devices not only for training, but also during daily routine, which requires "smartwatch" features to function properly.
  • You may be able to resolve one half of the dependency cycle by deregistering your callback when the view is hidden (onShow()/onHide()) or when the application exits (onStart()/onStop()).

    I haven't played with the mailbox system at all, so I can't say for sure if this works or not, but it seems logical.

    class TestView extends Ui.View {
    function initialize() {
    View.initialize();
    }

    // this code -->
    function onShow() {
    Comm.setMailboxListener(method(:onMail));
    }

    function onHide() {
    Comm.setMailboxListener(null);
    }
    // <-- this code

    function onMail(mailIter) {
    var logStr = null;
    var mailStr = mailIter.next();
    while( mailStr != null ) {
    logStr = mailStr;
    mailStr = mailIter.next();
    }
    Comm.emptyMailbox();
    Ui.requestUpdate();
    }
    }
  • Former Member
    Former Member over 8 years ago
    Thank yor for your advice, Travis, but I already tried that. Unfortunately, it didn't help to resolve this issue.