Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Picasso binding adapter 'a connection was leaked' message

I am using a binding adapter to load images in a recycler view. Images appear fine. While fast scrolling I noticed sometimes I was getting a 'connection leaked' message from Picasso.

The problem comes from dead image links, hardcoding all of my image urls to point nowhere produces the error for every image after scrolling the first couple off the screen.

W/OkHttpClient: A connection to https://s3-eu-west-1.amazonaws.com/ was leaked. Did you forget to close a response body?

The code is basically identical to this sample.

BindingUtils.kt

object BindingUtils {

@BindingAdapter("imageUrl")
@JvmStatic
fun setImageUrl(imageView: ImageView, url: String) {
    Picasso.with(imageView.context).load(url).into(imageView)
}

xml

<ImageView
android:id="@+id/imageview_merchant_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/primary"
android:scaleType="centerCrop"
app:imageUrl="@{viewModel.background}"/>

gradle

implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
implementation "com.squareup.retrofit2:adapter-rxjava2:$rootProject.retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okhttpLoggingVersion"
implementation "com.squareup.picasso:picasso:$rootProject.picassoVersion"

retrofitVersion = '2.3.0'
okhttpLoggingVersion = '3.6.0'
picassoVersion = '2.5.2'

I can see several references to people needing to closing connections for standard Okhttp requests but seeing as that Picasso load call is a one-liner how can this be leaking?

like image 239
Daniel Wilson Avatar asked Dec 11 '17 15:12

Daniel Wilson


1 Answers

Under the hood Picasso is using okhttp3 for handling its network requests. See here the code for Picasso's NetworkRequestHandler class: https://github.com/square/picasso/blob/0728bb1c619746001c60296d975fbc6bd92a05d2/picasso/src/main/java/com/squareup/picasso/NetworkRequestHandler.java

There is a load function that handles an okhttp Request:

@Override public Result load(Request request, int networkPolicy) throws IOException {
    okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
    Response response = downloader.load(downloaderRequest);
    ResponseBody body = response.body();

    if (!response.isSuccessful()) {
      body.close();
      throw new ResponseException(response.code(), request.networkPolicy);
    }

    // Cache response is only null when the response comes fully from the network. Both completely
    // cached and conditionally cached responses will have a non-null cache response.
    Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;

    // Sometimes response content length is zero when requests are being replayed. Haven't found
    // root cause to this but retrying the request seems safe to do so.
    if (loadedFrom == DISK && body.contentLength() == 0) {
      body.close();
      throw new ContentLengthException("Received response with 0 content-length header.");
    }
    if (loadedFrom == NETWORK && body.contentLength() > 0) {
      stats.dispatchDownloadFinished(body.contentLength());
    }
    InputStream is = body.byteStream();
    return new Result(is, loadedFrom);
  }

I am not too familiar with the Picasso project, but it seems like the response body object is not closed in all cases. You may have spotted a bug in Picasso and may want to file an issue at picasso's github

like image 197
donfuxx Avatar answered Nov 15 '22 03:11

donfuxx