user1622343
user1622343

Reputation: 969

How do I get Codenameone android App to reliably connect to REST API?

I have built a codename one android app that connects a restdb.io database backend using REST. However, I am having random network failures like below

>     [EDT] 0:4:26,949 - Exception: java.net.ConnectException - Failed to connect to ziemozi-a3ef.restdb.io/188.166.28.84:443
> java.net.ConnectException: Failed to connect to
> ziemozi-a3ef.restdb.io/188.166.28.84:443  at
> com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
>   at
> com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
>   at
> com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
>   at
> com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
>   at
> com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
>   at
> com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:302)
>   at
> com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:245)
>   at
> com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
>   at
> com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
>   at
> com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
>   at
> com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
>   at
> com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
>   at
> com.codename1.impl.android.AndroidImplementation.getResponseCode(AndroidImplementation.java:6113)
>   at
> com.codename1.io.ConnectionRequest.performOperationComplete(ConnectionRequest.java:905)
>   at
> com.codename1.io.NetworkManager$NetworkThread.run(NetworkManager.java:341)
>   at
> com.codename1.impl.CodenameOneThread$1.run(CodenameOneThread.java:60)
>   at java.lang.Thread.run(Thread.java:919)

OR

> java.net.SocketTimeoutException: timeout
    at com.android.okhttp.okio.Okio$3.newTimeoutException(Okio.java:214)
    at com.android.okhttp.okio.AsyncTimeout.exit(AsyncTimeout.java:263)
    at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:217)
    at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:307)
    at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:301)
    at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:197)
    at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:188)
    at com.android.okhttp.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:129)
    at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:770)
    at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:642)
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:475)
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
    at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
    at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
    at com.codename1.impl.android.AndroidImplementation.getResponseCode(AndroidImplementation.java:6113)
    at com.codename1.io.ConnectionRequest.performOperationComplete(ConnectionRequest.java:905)
    at com.codename1.io.NetworkManager$NetworkThread.run(NetworkManager.java:341)
    at com.codename1.impl.CodenameOneThread$1.run(CodenameOneThread.java:60)
    at java.lang.Thread.run(Thread.java:919)
Caused by: java.net.SocketException: socket is closed
    at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(ConscryptFileDescriptorSocket.java:554)
    at com.android.okhttp.okio.Okio$2.read(Okio.java:138)
    at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:213)

This happens in both simulator and device. And also when using fixed internet or mobile internet though it appears to happen more with mobile internet,

Upvotes: 2

Views: 86

Answers (1)

Francesco Galgani
Francesco Galgani

Reputation: 6249

Both of the logs you reported seem to refer to connectivity issues, rather than server-side errors. You can't avoid connectivity issues, in fact, you should assume them to be normal, especially on mobile devices. The question then is how to handle them.

There is no universally valid answer, it depends on what you want to achieve. What many apps do, in these cases, is to report the problem to the user while continuously trying to repeat the failed operation, i.e. in this case the REST request, until an HTTP code is received in response.

For this kind of approach, I had asked a question back in the day, you can read it here: Distinguish between server-side errors and connection problems

In short, I removed the default error handling code from init() and I invoke the following well tested and working method for the purpose (it is a version based on the code in Distinguish between server-side errors and connection problems), that you can customize according to your needs:

    private void addNetworkAndServerErrorListener() {
        // The following way to manage network errors is discussed here:
        // https://stackoverflow.com/questions/61993127/distinguish-between-server-side-errors-and-connection-problems
        addNetworkErrorListener(err -> {
            // prevents the event from propagating
            err.consume();

            if (err.getError() != null) {
                // this is the case of a network error,
                // like: java.io.IOException: Unreachable
                Log.p("Error connectiong to: " + err.getConnectionRequest().getUrl(), Log.ERROR);
                // maybe there are connectivity issues, let's try again
                ToastBar.showInfoMessage("Reconnect...");
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        err.getConnectionRequest().retry();
                    }
                }, 2000);
            } else {
                // this is the case of a server error
                // logs the error
                String errorLog = "REST ERROR\nURL:" + err.getConnectionRequest().getUrl()
                        + "\nMethod: " + err.getConnectionRequest().getHttpMethod()
                        + "\nResponse code: " + err.getConnectionRequest().getResponseCode();
                if (err.getConnectionRequest().getRequestBody() != null) {
                    errorLog += "\nRequest body: " + err.getConnectionRequest().getRequestBody();
                }
                if (err.getMetaData() != null) {
                    errorLog += "\nMetaData: " + err.getMetaData().toString();
                }
                if (err.getConnectionRequest().getResponseData() != null) {
                    errorLog += "\nResponse message: " + new String(err.getConnectionRequest().getResponseData());
                }
                if (err.getConnectionRequest().getResponseErrorMessage() != null) {
                    errorLog += "\nResponse error message: " + err.getConnectionRequest().getResponseErrorMessage();
                }
                if (err.getMessage() != null) {
                    errorLog += "\nResponse error message: " + err.getMessage();
                }
                Log.p(errorLog, Log.ERROR);

                Log.sendLogAsync();
                ToastBar.showErrorMessage("Server Error", 10000);
            }
        });
    }

Upvotes: 2

Related Questions