【问题标题】:Android Picasso Recyclerview Image downloading slowAndroid Picasso Recyclerview 图片下载慢
【发布时间】:2019-05-23 17:18:49
【问题描述】:

我有无穷无尽的 recyclerview,几乎可以模仿 Instagram 列表/提要。 Image 在哪里加载到全屏宽度的 Imageview。我正在使用毕加索加载图像。这是我的代码:

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);
}

}

平均图像大小为 80-100 kbs。但是图像下载时间太慢了。下载图像几乎需要 3-4 秒。另一方面,当从缓存加载图像时,它显然会立即加载图像。我从服务器获取 15 个项目以加载到 recyclerview,这意味着将并行下载 15 个图像。但与 Instagram 或 Facebook 提要相比,它仍然相当慢。

谁能指导我正确的技术以使其尽可能快,以便当用户向下滚动列表图像时应该已经加载而不是等待图像加载?

【问题讨论】:

  • 2 个问题:1) 您当前的互联网连接的上传/下载速度是多少?和2) 如果您编写一个简单的get 请求来ping 服务器返回这些图像(只需使用应用程序中的一个URL),或使用Postman 之类的东西,响应时间是多少毫秒?
  • @Silmarilos 如果我在浏览器上打开下载链接,它会在几毫秒内下载,图像部署在 aws s3 存储桶上,而且互联网连接速度非常快如果我使用 Facebook 等任何其他应用程序,它的图像加载速度很快
  • 使用通用图像加载器,它比毕加索快。还可以尝试使用 okhttp 下载器和 picasso

标签: android android-recyclerview android-adapter picasso android-lazyloading


【解决方案1】:

实际上,我已经使用 lib 很长时间了,没有任何网络问题。我试图分析代码并发现以下内容:

  1. 问题可能是转换由于lib首先转换传入的图像然后显示,请参考这部分

    毕加索.get()

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

建议:如果您尝试在圆形视图中显示图像。然后使用 CircularImageView,不要释放 I/O。

  • 用比你的稍微强大一点的设备检查性能

编辑

  • 检查您手机的网络使用情况、哪个应用正在消耗、多少兆字节,因为某些应用会在后台静默使用您的网络

  • 为什么在您的浏览器中速度很快?因为你的浏览器没有对图片做任何额外的操作

【讨论】:

    【解决方案2】:

    如果您的图片太大,则可能需要一些时间。所以 15 个 100kb 大小的图像几乎要加载 1.5 Mb

    如果您使用 fit()resize() 应该可以解决您的问题。我目前将数百个非常大的 jpg 文件加载到单个 GridLayout 中并且没有问题。

    编辑

    这可能是互联网问题,毕加索无法控制满足网络请求的服务器或互联网连接本身。使用 Thumbor 之类的服务可能会有所帮助:https://github.com/thumbor/thumbor

    【讨论】:

    • 我已经尝试过 fit()、resize() 和 centerInside() 等仍然需要时间,正如我所提到的,下载需要时间,一旦下载了图像,就会在下载后调整大小然后它不会从缓存中加载需要时间
    【解决方案3】:

    对于您的情况,您可以使用此代码流:

    1. 使用构造函数创建具有单例功能的 Feed 类(POJO 或模型),
    2. 当您的应用开始获得一些额外时间时,开始在异步后台任务中填充 Feeds 类, Feeds feed_ins = Feeds.getInstance(SomeData)
    3. 在 Feeds 类中,通过调用构造函数中定义的方法从 Picasso 获取位图并将其保存到列表中,List&lt;Bitmap&gt; imageBitmaps = new ArrayList&lt;&gt;()
    4. 在 Feeds 类中创建一个方法,该方法在调用时返回特定位图,并使用 poitsion getImageBitmap(int position){ return imageBitmaps.get(position) }
    5. 在 HomeAdapter 获取 Feeds 类实例,Feeds feed_ins = Feeds.getInstance();
    6. 现在在 HomeAdapter 类中使用 Feeds 类单例直接获取位图, ivThumbnail.setImageBitmap(feed_ins.getImageBitmap(int position))

    这将通过预加载图像来解决您的加载时间问题。

    【讨论】:

      【解决方案4】:

      你能试着把你的持有者类放在static。每个findViewById 在性能上都有实际的成本。如您所见,您的班级有很多findViewById。另一方面,我建议看一下 Picasso 和 Glide 之间的区别,它们在管理图像的方式上存在差异。

      当我尝试从 URL 下载图像时,Picasso 从 Internet 加载图像的速度比 Glide 快得多。可能是因为毕加索下载完图片后,直接把全尺寸图片推送到内存了;而 Glide 根据 ImageView 的尺寸调整图像的大小。

      https://medium.com/@multidots/glide-vs-picasso-930eed42b81d

      【讨论】:

        【解决方案5】:

        请检查这些可能对您有用的答案。

        Load large images with Picasso and custom Transform object

        How to increase speed in Picasso

        How to make Picasso load images faster?

        您可以改进此代码:

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

        每次onBindViewHolder 调用都会创建新的占位符和转换。因此,您可以为这种情况创建局部变量。像这样的..

        public class HomeAdapter extends RecyclerView.Adapter {
        
        Context context;
        private final CircleTransform circleTransform;
        private final ColorDrawable placeholderDrawable;
        
        .
        .
        .
        
        public HomeAdapter(Context context, List<Feed> feeds) {
            this.context = context;
            this.feeds = feeds;
            this.circleTransform = new CircleTransform();
            this.placeholderDrawable = new ColorDrawable(ContextCompat.getColor(context, R.color.colorGreyLight))
        }
        

        用法:

        Picasso.get()
                .load(designFeed.getDesignCategory().getThumbnail())
                .transform(circleTransform)
                .placeholder(placeholderDrawable)
                .into(designItemHolder.ivCategory);
        

        我还建议您查看rguzman's answer。此调整也提高了性能。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-09-20
          • 1970-01-01
          • 1970-01-01
          • 2020-07-11
          • 2018-05-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多