user1483208
user1483208

Reputation: 385

Picasso in list adapter is not loading images from cache

I'm using picasso to load images from server in my list adapter. When I'm scrolling list up and down Picasso keeps allocate more and more memory, screen below: enter image description here

Here is part of my list adapter:

 @Override
    public View getView(final int position, View convertView, ViewGroup viewGroup) {
        ViewHolder viewHolder;

        if (convertView == null || convertView.getTag() == null) {
            //init list item
            viewHolder = new ViewHolder();

            LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.subfood_item, viewGroup,
                    false);

            viewHolder.mImgvPicture = (ImageView) convertView.findViewById(R.id.imgvPicture);
            viewHolder.mTvHeader = (TextView) convertView.findViewById(R.id.tvHeader);
            viewHolder.mTvDescription = (TextView) convertView.findViewById(R.id.tvDescription);

            convertView.setTag(viewHolder);
        } else {
            //list item is used again - get all references for view
            viewHolder = (ViewHolder) convertView.getTag();
        }
        Product product = mItems.get(position);

        viewHolder.mTvHeader.setText(product.getName());
        viewHolder.mTvDescription.setText(product.getDescription());


        if (product.isAsset()) {
            String folderPath = "file:///android_asset/product/";
            Picasso.with(mContext).load(folderPath + product.getPath()).fit().centerCrop().
                    into(viewHolder.mImgvPicture);
        } else {
            RestUtils.loadImageWithPicasso(RestUtils.URL_IMAGE_FROM_PRODUCT + product.getNetId(), viewHolder.mImgvPicture);
        }


        return convertView;
    }

First part - loading from assets is fine. Problem is with:

RestUtils.loadImageWithPicasso(RestUtils.URL_IMAGE_FROM_PRODUCT + product.getNetId(), viewHolder.mImgvPicture);

I've wrapped this with additional method because of server authentication and offline caching. Below is code of RestUtils.loadImageWithPicasso

 public static void loadImageWithPicasso(String url, ImageView imageView) {
        float dp = App.getContext().getResources().getDisplayMetrics().density;
        if(dp<=4.0f){
            url = url+"?size="+(int)Math.floor(dp);
        }  else {
            url = url+"?size=4";
        }

        String requestUrl = RestUtils.getUrl(url);

        OkHttpClient picassoClient = new OkHttpClient();

        final String finalUrl = url;
        String hash = RestUtils.getHashed(finalUrl);
        Interceptor interceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request newRequest = chain.request().newBuilder()
                        .addHeader(RestUtils.HEADER, RestUtils.getHashed(finalUrl))
                        .build();
                return chain.proceed(newRequest);
            }
        };

        picassoClient.interceptors().add(interceptor);

        File httpCacheDir = new File(App.getContext().getExternalCacheDir(), RestUtils.IMAGE_CACHE);
        long httpCacheSize = 40 * 1024 * 1024;
        Cache cache = new Cache(httpCacheDir, httpCacheSize);
        picassoClient.setCache(cache);

        Picasso picasso = new Picasso.Builder(App.getContext()).downloader(new OkHttpDownloader(picassoClient)).build();

        picasso.load(requestUrl)
                .into(imageView);
    }

Even if I quit from fragment, memory is still allocated.

Upvotes: 0

Views: 625

Answers (1)

iagreen
iagreen

Reputation: 32026

Not sure you can blame picasso on this one. You are building a custom Picasso instance with a new cache on every image load. Every time you call loadImageWithPicasso, your code is allocating at least the following new objects -OkHttpClient, Interceptor, File, Cache, Picasso and at least two String's. You should only be building your Picasso instance once.

There are a couple ways you choose to do that. One is -

Divide loadImageWithPicasso into two methods createCustomPicasso that returns your custom picasso object, call that once in your UI code and save in a member variable.

Then, modify loadImageWithPicasso so that it takes a picasso instance you can pass in --

public static void loadImageWithPicasso(Picasso picasso, String url, ImageView imageView) 

and then have it do the url-specific modifications before issuing the requests.

As a last note, your Interceptor should be getting the data it needs to modify the request out of chain.getRequest() instead of using data from outside its immediate scope. You are referencing finalUrl inside your interceptor. It like can be replaced with chain.getRequest().urlString() See the documentation on interceptors for a good walk through on the usage.

Upvotes: 2

Related Questions