OkHttp: предупреждение об утечке соединения

Я использую OkHttp 3 и продолжаю получать предупреждения об утечке соединения:

WARNING: A connection to https://help.helpling.com/ was leaked. Did you forget to close a response body?
Jul 14, 2016 6:57:09 PM okhttp3.ConnectionPool pruneAndGetAllocationCount

Каждый раз, когда я получаю ResponseBody, я либо вызываю .string(), который предположительно закрывает для меня поток, либо явно закрываю его в блоке finally следующим образом:

ResponseBody responseBody = response.body();
try (Reader responseReader = responseBody.charStream()) {
    ...
}
finally {
    responseBody.close();
}

Мое приложение интенсивно использует сеть, и тем не менее это предупреждение появляется часто. Я никогда не наблюдал никаких проблем, вызванных этой предполагаемой утечкой, но мне все же хотелось бы понять, если и что я делаю неправильно.

Может ли кто-нибудь пролить свет на это?


person Alphaaa    schedule 14.07.2016    source источник


Ответы (2)


При обновлении до OkHttp 3.7 Eclipse начал предупреждать меня о потенциальных утечках ресурсов. Я обнаружил, что моя проблема заключается в этом методе, который я написал:

public static Response getResponse(HttpUrl url, OkHttpClient client) throws IOException {
    Builder request = new Request.Builder().url(url);
    Response response = client.newCall(request.build()).execute();
    if (!response.isSuccessful()) {
        boolean repeatRequest = handleHttpError(response);
        if (repeatRequest)
            return getResponse(url, client, etag);
        else
            throw new IOException(String.format("Cannot get successful response for url %s", url));
    }
    return response;
}

Я предполагал, что при постоянном вызове getResponse(url, client).body().string() поток автоматически закрывается. Но всякий раз, когда ответ был неудачным, перед выполнением .string() возникало исключение, поэтому поток оставался открытым.

Добавление явного закрытия в случае неудачного ответа решило проблему.

if (!response.isSuccessful()) {
    boolean repeatRequest = handleHttpError(response);
    response.close();
}
person Alphaaa    schedule 05.05.2017

Как упоминалось в других ответах, вы должны закрыть ответ. Чуть чище было бы объявить ResponseBody в блоке try, чтобы он автоматически закрывался.

try(ResponseBody body = ....){
....
}
person Corrigan Johnson    schedule 15.12.2016
comment
Для некоторых может быть лучше закрыть Response, а не ResponseBody напрямую. То есть закрытие Response закрывает базовый ResponseBody... для моих вариантов использования закрытие Response работает немного лучше. - person Rob Bygrave; 09.01.2017
comment
Я не мог использовать подход «попробуй с ресурсами», потому что мой метод должен был вернуть экземпляр Response для чтения другим методом в случае успеха (см. мой собственный ответ). - person Alphaaa; 05.05.2017
comment
если вы используете Kotlin и новый okhttpclient v4, просто используйте ответ, используя client.newCall(..).execute().use {response -> ... } - person Jilles van Gurp; 30.07.2019