SocketTimeoutException timeout in Android

2019-09-14 19:14发布

问题:

I'm fetching JSON data from the web via GET request. The code is the following, even though the problems seems to be "conceptual".

/**
 * Make an HTTP request to the given URL and return a String as the response.
 */
public static String makeHttpRequest(URL url) throws IOException {
    String jsonResponse = "";

    //Check that url is not null
    if(url == null){
        return jsonResponse;
    }

    HttpURLConnection urlConnection = null;
    InputStream inputStream = null;
    try {
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestMethod("GET");
      urlConnection.setReadTimeout(10000 /* milliseconds */);
           urlConnection.setConnectTimeout(15000 /* milliseconds */);
         urlConnection.connect();

        //If the request was successfull (code 200)
        //then read the imput string and parse the response
        if(urlConnection.getResponseCode() == 200) {
            inputStream = urlConnection.getInputStream();
            jsonResponse = readFromStream(inputStream);
        }
        else {
            Log.e(LOG_TAG_MAIN, "Error Response code" + urlConnection.getResponseCode());
        }

    } catch (IOException e) {
        Log.e(LOG_TAG_MAIN, "Problem the JSON results", e);
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        if (inputStream != null) {
            // Closing the input stream could throw an IOException, which is why
            // the makeHttpRequest(URL url) method signature specifies than an IOException
            // could be thrown.
            inputStream.close();
        }
    }
    return jsonResponse;
}
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
/**
 * Convert the {@link InputStream} into a String which contains the
 * whole JSON response from the server.
 */
public static String readFromStream(InputStream inputStream) throws IOException {
    StringBuilder output = new StringBuilder();
    if (inputStream != null) {
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
        BufferedReader reader = new BufferedReader(inputStreamReader);
        String line = reader.readLine();
        while (line != null) {
            output.append(line);
            line = reader.readLine();
        }
    }
    return output.toString();
}

Indeed the following code worked for months, but suddenly started throwing:

E/com.example.android.abcfolio.MainActivity: Problem the JSON results
                                             java.net.SocketTimeoutException: timeout
 at com.android.okhttp.okio.Okio$3.newTimeoutException(Okio.java:207)
 at com.android.okhttp.okio.AsyncTimeout.exit(AsyncTimeout.java:250)
 at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:217)
 at com.android.okhttp.okio.RealBufferedSource.request(RealBufferedSource.java:71)
 at com.android.okhttp.okio.RealBufferedSource.require(RealBufferedSource.java:64)
 at com.android.okhttp.okio.RealBufferedSource.readHexadecimalUnsignedLong(RealBufferedSource.java:270)
 at com.android.okhttp.internal.http.HttpConnection$ChunkedSource.readChunkSize(HttpConnection.java:479)
 at com.android.okhttp.internal.http.HttpConnection$ChunkedSource.read(HttpConnection.java:460)
 at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:50)
 at com.android.okhttp.okio.RealBufferedSource.exhausted(RealBufferedSource.java:60)
 at com.android.okhttp.okio.InflaterSource.refill(InflaterSource.java:101)
 at com.android.okhttp.okio.InflaterSource.read(InflaterSource.java:62)
 at com.android.okhttp.okio.GzipSource.read(GzipSource.java:80)
 at com.android.okhttp.okio.RealBufferedSource$1.read(RealBufferedSource.java:349)
 at java.io.InputStreamReader.read(InputStreamReader.java:233)
 at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
 at java.io.BufferedReader.readLine(BufferedReader.java:397)
 at com.example.android.abcfolio.utils.NetworkCommonUtils.readFromStream(NetworkCommonUtils.java:102)
 at com.example.android.abcfolio.utils.NetworkCommonUtils.makeHttpRequest(NetworkCommonUtils.java:68)
 at com.example.android.abcfolio.utils.NetworkCoinMarketCapUtils.fetchCoinMarketCapData(NetworkCoinMarketCapUtils.java:44)
 at com.example.android.abcfolio.sync.CoinsSyncTask.syncCoins(CoinsSyncTask.java:65)
 at com.example.android.abcfolio.sync.CoinsFirebaseJobService$1.doInBackground(CoinsFirebaseJobService.java:64)
 at com.example.android.abcfolio.sync.CoinsFirebaseJobService$1.doInBackground(CoinsFirebaseJobService.java:53)
 at android.os.AsyncTask$2.call(AsyncTask.java:295)
 at java.util.concurrent.FutureTask.run(FutureTask.java:237)
 at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
 at java.lang.Thread.run(Thread.java:818)

Since, I never touched this code, but I've only added other functionality in other parts of the code, I don't understand what's going on. I've tried to raise the timeout times, but the error actually shows up immediately, so increasing the time seems to be useless. I exclude that this is due to the size of the request, since even if I ask for only a subset of all the JSON it still complains.

It's possible that the problem is in the website that I'm querying? Maybe they have too many request at the moment? Of maybe my phone that I use for developing has been banned for too many requests?

EDIT with additional info: 1) The problems is intermittent. Sometimes the app works fine and all the data are loaded correctly. 2)Sometimes this messages shows up in the LogCat, I'm not sure if is related (the fmradio.jar in particular):

03-12 12:49:36.600 16457-16457/? W/System: ClassLoader referenced unknown path: /data/app/com.example.android.abcfolio-2/lib/arm64
03-12 12:49:36.603 16457-16457/? I/InstantRun: Instant Run Runtime started. Android package is com.example.android.abcfolio, real application class is null.

                                               [ 03-12 12:49:36.607 16457:16457 W/         ]
                                               Unable to open '/system/framework/qcom.fmradio.jar': No such file or directory
03-12 12:49:36.607 16457-16457/? W/art: Failed to open zip archive '/system/framework/qcom.fmradio.jar': I/O Error

                                        [ 03-12 12:49:36.607 16457:16457 W/         ]
                                        Unable to open '/system/framework/oem-services.jar': No such file or directory
03-12 12:49:36.607 16457-16457/? W/art: Failed to open zip archive '/system/framework/oem-services.jar': I/O Error
03-12 12:49:37.149 16457-16470/? I/art: Debugger is no longer active
03-12 12:49:39.030 16457-16457/com.example.android.abcfolio W/System: ClassLoader referenced unknown path: /data/app/com.example.android.abcfolio-2/lib/arm64
03-12 12:49:43.962 16457-16457/com.example.android.abcfolio W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable

3)There other multiple warnings like

W/PathParser: Points are too far apart 4.000000000000003 

These should be due to ConstraintLayout. It is possible that it is using too much memory to perform graphic work on the main thread and this turns into errors in the http request? (it seems unlikely to me, but it could explain why this problem is showing up now, after I've inserted many views with ConstraintLayout)

EDIT 2: 4) In the app I'm fetching the data in two different ways: one with a simple Loader that extends AsyncTaskLoader, and another one with a Firebase Job Service. Is the second one that gives problems, even though they fetch the same API and use both the two methods written above.

EDIT 3: 5) I'm now sure that the problem is not due to changes made recently in the code, since even an older backup version of the app behaves in the same way. 6) Now it's throwing this:

Problem parsing the JSON results
                                             java.io.EOFException
                                                 at com.android.okhttp.okio.RealBufferedSource.require(RealBufferedSource.java:64)
                                                 at com.android.okhttp.okio.RealBufferedSource.readHexadecimalUnsignedLong(RealBufferedSource.java:270)
                                                 at com.android.okhttp.internal.http.HttpConnection$ChunkedSource.readChunkSize(HttpConnection.java:479)
                                                 at com.android.okhttp.internal.http.HttpConnection$ChunkedSource.read(HttpConnection.java:460)
                                                 at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:50)
                                                 at com.android.okhttp.okio.RealBufferedSource.exhausted(RealBufferedSource.java:60)
                                                 at com.android.okhttp.okio.InflaterSource.refill(InflaterSource.java:101)
                                                 at com.android.okhttp.okio.InflaterSource.read(InflaterSource.java:62)
                                                 at com.android.okhttp.okio.GzipSource.read(GzipSource.java:80)
                                                 at com.android.okhttp.okio.RealBufferedSource$1.read(RealBufferedSource.java:349)
                                                 at java.io.InputStreamReader.read(InputStreamReader.java:233)
                                                 at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
                                                 at java.io.BufferedReader.readLine(BufferedReader.java:397)
                                                 at com.example.android.abcfolio.utils.NetworkCommonUtils.readFromStream(NetworkCommonUtils.java:106)
                                                 at com.example.android.abcfolio.utils.NetworkCommonUtils.makeHttpRequest(NetworkCommonUtils.java:72)
                                                 at com.example.android.abcfolio.utils.NetworkCoinMarketCapUtils.fetchCoinMarketCapData(NetworkCoinMarketCapUtils.java:44)
                                                 at com.example.android.abcfolio.sync.CoinsSyncTask.syncCoins(CoinsSyncTask.java:89)
                                                 at com.example.android.abcfolio.sync.CoinsSyncIntentService.onHandleIntent(CoinsSyncIntentService.java:33)
                                                 at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:66)
                                                 at android.os.Handler.dispatchMessage(Handler.java:102)
                                                 at android.os.Looper.loop(Looper.java:148)
                                                 at android.os.HandlerThread.run(HandlerThread.java:61)

EDIT 4: 6) I've logged urlConnection.getResponseCode() + urlConnection.getResponseMessage() and sometimes, instead of the success code 200, it returns: 504Gateway Time-out.

回答1:

The server is taking longer to service your request than you originally expected when you set the timeouts. They are rather short. They should be in minutes, not seconds.

It doesn't have anything to do with what else your application is doing.



回答2:

After many trials, I've concluded indeed that the problem is server side, so there is no real way to solve it. What we can do is to handle gracefully the exception.