Search code examples
codenameone

Codenameone - When to use callSerially with Dialogs?


In the answer here and in section 7.2.1 of the Codenameone Developer Guide, it is suggested that dialogs should be called using callSerially. I assume that means instead of this:

                    dlg.showDialog();

we should use:

                Display.getInstance().callSerially(() -> {
                    dlg.showDialog();
                });

But I notice in various other parts of Codenameone code that Dialog.shows() are not wrapped in callSerially, for example ConnectionRequest.java, NetworkManager.java and XFClient.java. Should those be using callSerially as suggested? If not, what is the criteria to decide when to use callSerially for Dialogs and when to not?

Background: I’m asking this question because after implementing the networking retry logic discussed here my users are having some intermittent (and so far impossible to reliably replicate) app lock ups which I suspect may be related to conflict between my Yes/No dialog and a custom network issues dialog in XFClient (see below) that may happen concurrently. I am wondering if I should always be using callSerially for these dialogs.

protected void setupConnection(ConnectionRequest req) {
        if (timeout > 0) {
            req.setTimeout(timeout);
        }
        if (readTimeout > 0) {
            req.setReadTimeout(readTimeout);
        }

        // remainder is custom code I added..

        if (silentRetryCount > 0) {     
            req.setSilentRetryCount(silentRetryCount);
        }
        req.addExceptionListener(evt -> {    
            if (evt != null) {
                if (req.getSilentRetryCount() > 0) {
                    //  silentRetryCount--;
                    req.setSilentRetryCount(req.getSilentRetryCount() - 1);
                    //  NetworkManager.getInstance().resetAPN();      // private, not sure if we need this?
                    req.retry();
                    return;
                }
                Exception exc = evt.getError();
                Log.e(exc);
                if (Display.isInitialized() && !Display.getInstance().isMinimized()
                        //                        && Dialog.show("Exception", exc.toString() + ": for URL " + url + "\n" + exc.getMessage(), "Retry", "Cancel")) {
                        && Dialog.show("Network Issue", "Hopefully it is just a bump in the road.  Suggest you retry...", "Retry", "Cancel")) {
                    req.retry();
                }
                // note: ConnectionRequest.handleException has an else block here setting retrying= false and killed=true

            }
        });
    }

Note: The added code is modelled after the handleException method in ConnectionRequest.java. I couldn't figure out how to add the resetAPN and else block so left those out. Not sure if that was a mistake?

Any help appreciated.


Solution

  • callSerially for dialogs should be used under two circumstances:

    • You're off the EDT - this is required
    • You're in an event chain and want the current event to flush. This is a complex edge case. I won't get into it since it doesn't apply for this case

    Initially when the network manager logic was implemented we wrote the retry code. We didn't notice the EDT violation back then and it didn't manifest as much. Unfortunately as is the case with many bugs of this sort it's hard to get rid of it now.

    There are other solutions for this but one of the better solutions is to use the network error handler globally which is far closer to what you want. Thisquestion covers the current usage for it: Distinguish between server-side errors and connection problems