【问题标题】:Recycler view with Picasso taking more memory毕加索占用更多内存的回收站视图
【发布时间】:2016-08-10 03:43:30
【问题描述】:

我正在使用 RecyclerView 和 Picasso 库来显示带有一些文本数据的很长的图像列表。我使用分页加载更多记录,一旦加载更多记录,毕加索就开始下载图像。我面临的主要问题是,当所有不可见的回收器视图项目以及可见项目都保存在内存中并且它正在消耗大量内存时。

我想为所有不可见的项目释放内存,一旦用户滚动,毕加索就会开始加载图像(从网络或缓存)。

以下是我在应用中实现的示例代码:

/**
 * Adapter class used to display hotel list.
 * Created by gaganpreetsingh on 1/18/2016.
 */
public class HotelListAdapter extends RecyclerView.Adapter<HotelDetailViewHolder> {

    private Context mContext;
    private List<HotelModel> mModelList;
    private OnRecyclerItemClickListener mListener;

    public HotelListAdapter(Context context, List<HotelModel> modelList,
                            OnRecyclerItemClickListener listener) {
        this.mContext = context;
        this.mModelList = modelList;
        mListener = listener;
    }

    @Override
    public HotelDetailViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.layout_hotel_item, parent, false);
        return new HotelDetailViewHolder(view, mListener);
    }

    @Override
    public void onBindViewHolder(HotelDetailViewHolder holder, int position) {
        holder.setItem(mContext, mModelList.get(position));
    }

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

}

下面是ViewHolder类的实现:

/**
 * View holder class used to display a hotel with
 * location and price.
 * Created by gaganpreetsingh on 1/18/2016.
 */
public class HotelDetailViewHolder extends RecyclerView.ViewHolder
        implements
        View.OnClickListener {

    private static final int MAX_WIDTH = 320;
    private static final int MAX_HEIGHT = 186;

    private ImageView mImgHotel;
    private TextView mTxtHotelName;
    private TextView mTxtAreaName;
    private TextView mTxtHotelDistance;
    private TextView mTxtPrice;

    private View mRootView;

    private OnRecyclerItemClickListener mListener;

    private int mWidth, mHeight;

    /**
     * Initialize view holder
     *
     * @param itemView to initialize
     */
    public HotelDetailViewHolder(View itemView, OnRecyclerItemClickListener listener) {
        super(itemView);
        mRootView = itemView;
        mListener = listener;
        mImgHotel = (ImageView) mRootView.findViewById(R.id.img_hotel);
        mTxtHotelName = (TextView) mRootView.findViewById(R.id.txt_hotel_name);
        mTxtAreaName = (TextView) mRootView.findViewById(R.id.txt_area_name);
        mTxtHotelDistance = (TextView) mRootView.findViewById(R.id.txt_hotel_distance);
        mTxtPrice = (TextView) mRootView.findViewById(R.id.txt_price);
        mRootView.setOnClickListener(this);
    }

    /**
     * Set the specified hotel details to this
     * view holder
     *
     * @param model to set
     */
    public void setItem(final Context context, final HotelModel model) {

        ViewTreeObserver vto = mImgHotel.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                mImgHotel.getViewTreeObserver().removeOnPreDrawListener(this);
                final int height = mImgHotel.getMeasuredHeight();
                final int width = mImgHotel.getMeasuredWidth();

                // get the target image view width and height
                final int heightDp = AppUtils.getDpFromPx(height, context);
                final int widthDp = AppUtils.getDpFromPx(width, context);

                // if target image has higher size than our max image size,
                // load image with max allowed size.
                if (heightDp > MAX_HEIGHT || widthDp > MAX_WIDTH) {
                    // download resized image
                    PicassoUtils.loadImage(context, model.mImage, mImgHotel, MAX_WIDTH, MAX_HEIGHT);
                } else {
                    // download resized image
                    PicassoUtils.loadImage(context, model.mImage, mImgHotel, widthDp, heightDp);
                }

                return true;
            }
        });


//        PicassoUtils.loadImage(context, model.mImage, mImgHotel);
        mTxtHotelName.setText(model.mName);
        mTxtAreaName.setText(model.mArea);

       }

    @Override
    public void onClick(View v) {
        if (mListener != null)
            mListener.onClick(mRootView, getAdapterPosition());
    }
}

这是使用毕加索加载图像的代码:

public static void loadImage(Context context, String url, ImageView imageView,
                                 int width, int height) {
        Picasso.with(context).load(url)
                .error(R.drawable.thumb_photo)
                .placeholder(R.drawable.thumb_photo)
                .resize(width, height)
                .into(imageView);
    }

此外,即使在加载调整大小的图像后,我有时也会遇到 OOM 异常。

请建议我一些方法来释放不可见项目的内存并避免 OOM 异常。

【问题讨论】:

  • 我也遇到了类似的问题,你解决了吗?
  • 在下面查看我的答案。

标签: android optimization memory-management android-recyclerview picasso


【解决方案1】:

我已通过下载与目标 ImageView 尺寸相同的调整大小的图像解决了这个问题。为了计算 ImageView 的实际尺寸,我使用了 ViewTreeObserver

下面是在ViewHolder中使用Picasso下载图片的示例代码:

ViewTreeObserver vto = mImgHotel.getViewTreeObserver();
            if(vto == null || !vto.isAlive()) return;
            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                public boolean onPreDraw() {
                    mImgHotel.getViewTreeObserver().removeOnPreDrawListener(this);
                    final int height = mImgHotel.getMeasuredHeight();
                    final int width = mImgHotel.getMeasuredWidth();

                    // get the target image view width and height
                    final int heightDp = AppUtils.getDpFromPx(height, context);
                    final int widthDp = AppUtils.getDpFromPx(width, context);

                    // download resized image
                    PicassoUtils.loadImage(context, model.mImage, mImgHotel, widthDp, heightDp);

                    return true;
                }
            });

【讨论】:

  • 每次我用毕加索加载图像时,听起来都需要做很多额外的工作,你确定这很有效吗?
  • 我通过实现这段代码主要解决了OOM异常。现在我使用 Picasso 的 OOM 异常为零。
  • 如何在我只是调用 Picasso.with.load.into ... 等的适配器中实现这一点。此外,如果我有 100 幅图像加载,这是否有意义适用于一个一个一个一个?
  • 无论你在哪里调用 Picasso.with.load,都写上面的代码。它将为您提供要加载的目标图像大小的确切尺寸。由于此代码是 ViewHolder 的一部分,因此将一一应用。
猜你喜欢
  • 2017-10-30
  • 2018-10-03
  • 1970-01-01
  • 2017-01-27
  • 1970-01-01
  • 2017-06-15
  • 2016-09-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多