Monday, 27 May 2019

Android Picasso Recyclerview Image downloading slow

I have endless recyclerview that almost mimics Instagram list/feeds. Where Image is loading into full screen width Imageview. I am using Picasso to load images. Here is my code:

public class HomeAdapter extends RecyclerView.Adapter {

Context context;

private Contract contract;

List<Feed> feeds;

static final int ITEM_TYPE_HEADER = 0;

static final int ITEM_TYPE_LOAD_MORE = 1;

static final int ITEM_TYPE_DESIGN = 2;

public HomeAdapter(Context context, List<Feed> feeds) {
    this.context = context;
    this.feeds = feeds;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View row;
    if (viewType == ITEM_TYPE_HEADER) {
        row = inflater.inflate(R.layout.list_item_home_header, parent, false);
        return new HeaderHolder(row);
    } else if (viewType == ITEM_TYPE_LOAD_MORE) {
        row = inflater.inflate(R.layout.list_item_load_more, parent, false);
        return new LoadMoreHolder(row);
    } else if (viewType == ITEM_TYPE_DESIGN) {
        row = inflater.inflate(R.layout.list_item_feed_home, parent, false);
        return new DesignItemHolder(row);
    }
    return null;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    Feed feed = feeds.get(position);
    if (holder instanceof HeaderHolder) {
        HeaderHolder headerHolder = (HeaderHolder) holder;
        Header header = (Header) feed;
        headerHolder.tvTitle.setText(header.getTitle());
        headerHolder.tvDescription.setText(header.getDescription());

    } else if (holder instanceof LoadMoreHolder) {
        LoadMoreHolder loadMoreHolder = (LoadMoreHolder) holder;
        LoadMore loadMore = (LoadMore) feed;
        if(loadMore.getType() == LOAD_MORE_TYPE_PROGRESS) {
            loadMoreHolder.pbLoadMore.setVisibility(View.VISIBLE);
            loadMoreHolder.rlMessageRetry.setVisibility(View.GONE);
        } else {
            loadMoreHolder.pbLoadMore.setVisibility(View.GONE);
            loadMoreHolder.rlMessageRetry.setVisibility(View.VISIBLE);
            if(StringUtils.isValid(loadMore.getTitle())) {
                loadMoreHolder.tvTitle.setText(loadMore.getTitle());
                loadMoreHolder.tvTitle.setVisibility(View.VISIBLE);
            } else
                loadMoreHolder.tvTitle.setVisibility(View.GONE);

            if(StringUtils.isValid(loadMore.getDescription())) {
                loadMoreHolder.tvDescription.setText(loadMore.getDescription());
                loadMoreHolder.tvDescription.setVisibility(View.VISIBLE);
            } else
                loadMoreHolder.tvDescription.setVisibility(View.GONE);
        }

        loadMoreHolder.btnRetry.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onRetryLoadMoreClick();
            }
        });

    } else if (holder instanceof DesignItemHolder) {
        final DesignItemHolder designItemHolder = (DesignItemHolder) holder;
        final DesignFeed designFeed = (DesignFeed) feed;
        String title = getFeedTitle(designFeed.getDesignCategory().getTitle(), designFeed.getEvent().getTitle());
        designItemHolder.tvCategory.setText(StringUtils.toUpperCaseSentence(designFeed.getDesignCategory().getTitle()));
        designItemHolder.tvDate.setText(DateTimeUtils.formatTime(designFeed.getDate()));
        designItemHolder.tvTitle.setText(StringUtils.toUpperCaseSentence(title));
        designItemHolder.tvBrand.setText(getBrandTitle(designFeed.getBrand().getTitle()));

        if(StringUtils.isValid(designFeed.getShopLink())) {
            designItemHolder.tvShop.setVisibility(View.VISIBLE);
            if(StringUtils.isValid(designFeed.getPrice())) {
                designItemHolder.tvPrice.setVisibility(View.VISIBLE);
                designItemHolder.tvPrice.setText(designFeed.getPrice() + " PKR");
            } else
                designItemHolder.tvPrice.setVisibility(View.GONE);

            designItemHolder.tvShop.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(contract != null)
                        contract.onOpenShopLink(designFeed.getShopLink());
                }
            });
        }
        else {
            designItemHolder.tvPrice.setVisibility(View.GONE);
            designItemHolder.tvShop.setVisibility(View.GONE);
        }

        showFeedImage(designFeed, designItemHolder.ivThumbnail, designItemHolder.progressBar);

        Picasso.get()
                .load(designFeed.getDesignCategory().getThumbnail())
                .transform(new CircleTransform())
                .placeholder(new ColorDrawable(ContextCompat.getColor(context, R.color.colorGreyLight)))
                .into(designItemHolder.ivCategory);

        designItemHolder.ivThumbnail.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onDesignFeedClick(designFeed);
            }
        });

        designItemHolder.tvCategory.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onDesignCategoryClick(designFeed.getDesignCategory());
            }
        });

        designItemHolder.ivCategory.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onDesignCategoryClick(designFeed.getDesignCategory());
            }
        });

        designItemHolder.tvTitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onDesignCategoryClick(designFeed.getDesignCategory());
            }
        });

        designItemHolder.tvBrand.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onBrandClick(designFeed.getBrand());
            }
        });

        designItemHolder.ibFavourite.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onFavouriteClick(designFeed);
            }
        });

        designItemHolder.ibShare.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onShareClick(designFeed);
            }
        });

        designItemHolder.ibDownload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(contract != null)
                    contract.onDownloadClick(designFeed);
            }
        });
    }
}

private void showFeedImage(final DesignFeed designFeed, final ImageView ivThumbnail, final ProgressBar progressBar) {
    Picasso.get()
            .load(designFeed.getThumbnail())
            .placeholder(new ColorDrawable(ContextCompat.getColor(context, R.color.colorGreyLight)))
            .networkPolicy(NetworkPolicy.OFFLINE)
            .into(ivThumbnail, new Callback() {
                @Override
                public void onSuccess() {
                }

                @Override
                public void onError(Exception e) {
                    //Try again online if cache failed
                    Picasso.get()
                            .load(designFeed.getThumbnail())
                            .placeholder(new ColorDrawable(ContextCompat.getColor(context, R.color.colorGreyLight)))
                            .into(ivThumbnail, new Callback() {
                                @Override
                                public void onSuccess() {
                                }

                                @Override
                                public void onError(Exception e) {

                                }
                            });
                }
            });
}

@Override
public int getItemViewType(int position) {
    Feed feed = feeds.get(position);
    if (feed instanceof Header)
        return ITEM_TYPE_HEADER;
    else if (feed instanceof LoadMore)
        return ITEM_TYPE_LOAD_MORE;
    else if (feed instanceof DesignFeed)
        return ITEM_TYPE_DESIGN;
    else
        return super.getItemViewType(position);
}

@Override
public int getItemCount() {
    return feeds.size();
}

public void insertFeedAtStart(Feed feed) {
    feeds.add(0, feed);
    notifyDataSetChanged();
}

public void updateFeeds(ArrayList<Feed> mFeeds) {
    feeds.addAll(mFeeds);
    notifyDataSetChanged();
}

public void addLoadMoreItem(Feed loadMoreFeed) {
    feeds.add(loadMoreFeed);
    notifyItemInserted(getItemCount() - 1);
}

public void removeItem(int index) {
    feeds.remove(index);
    notifyDataSetChanged();
}

private class DesignItemHolder extends RecyclerView.ViewHolder {
    TextView tvTitle, tvCategory, tvBrand, tvDate, tvPrice, tvShop;
    ImageView ivCategory, ivThumbnail;
    ImageButton ibFavourite, ibShare, ibDownload;
    ProgressBar progressBar;

    DesignItemHolder(View itemView) {
        super(itemView);
        tvTitle = itemView.findViewById(R.id.tvTitle);
        tvCategory = itemView.findViewById(R.id.tvCategory);
        tvBrand = itemView.findViewById(R.id.tvBrand);
        tvDate = itemView.findViewById(R.id.tvDate);
        ivCategory = itemView.findViewById(R.id.ivCategory);
        ivThumbnail = itemView.findViewById(R.id.ivThumbnail);
        tvPrice = itemView.findViewById(R.id.tvPrice);
        tvShop = itemView.findViewById(R.id.tvShop);
        ibFavourite = itemView.findViewById(R.id.ibFavourite);
        ibShare = itemView.findViewById(R.id.ibShare);
        ibDownload = itemView.findViewById(R.id.ibDownload);
        progressBar = itemView.findViewById(R.id.progressBar);
    }
}

private class HeaderHolder extends RecyclerView.ViewHolder {
    TextView tvTitle, tvDescription;
    HeaderHolder(View itemView) {
        super(itemView);
        tvTitle = itemView.findViewById(R.id.tvTitle);
        tvDescription = itemView.findViewById(R.id.tvDescription);
    }
}

private class LoadMoreHolder extends RecyclerView.ViewHolder {
    ProgressBar pbLoadMore;
    RelativeLayout rlMessageRetry;
    TextView tvTitle, tvDescription;
    AppCompatButton btnRetry;

    LoadMoreHolder(View itemView) {
        super(itemView);
        pbLoadMore = itemView.findViewById(R.id.pbLoadMore);
        rlMessageRetry = itemView.findViewById(R.id.rlMessageRetry);
        tvTitle = itemView.findViewById(R.id.tvTitle);
        tvDescription = itemView.findViewById(R.id.tvDescription);
        btnRetry = itemView.findViewById(R.id.btnRetry);
    }
}

public void setContract(Contract contract) {
    this.contract = contract;
}

public interface Contract {

    void onDesignCategoryClick(DesignCategory designCategory);

    void onDesignFeedClick(DesignFeed designCategory);

    void onBrandClick(Brand brand);

    void onRetryLoadMoreClick();

    void onShopClick(String shopLink);

    void onFavouriteClick(DesignFeed designFeed);

    void onShareClick(DesignFeed designFeed);

    void onDownloadClick(DesignFeed designFeed);

    void onOpenShopLink(String shopLink);
}

}

Averrage image size is 80-100 kbs But image download time is too slow. It almost taskes 3-4 seconds to downlaod an image. On the other hand when images are loading from cache it obviously loads image in no time. I get 15 items from server to load into recyclerview which means 15 images would be downloading in parallel. But still it is quite slow as compared to Instagram or Facebook feeds. Can anyone guide me about the correct technique to make it as fast as possible, so that when user scrolls down the list images should be already loaded instead him waiting for image to load.



from Android Picasso Recyclerview Image downloading slow

No comments:

Post a Comment