使用Click界面时,RecylerView适配器导致内存泄漏

时间:2016-03-17 12:39:35

标签: java android memory-leaks android-recyclerview

在我的RecyclerView.Adapter我定义了这个界面:

public interface OnItemClickListener {
    void onItemClick(View view, int position);
}

包含RecyclerView的片段实现了此接口。 在4或5个方向更改后, LeakCanary 会报告内存泄漏:

我的Fragment看起来像这样:

public class ImagesFragment extends Fragment implements ImageAdapter.OnItemClickListener {

    private static final String IMAGES_FRAGMENT_TAG = "ImagesFragment";
    private int SPAN_COUNT = 2;

    private String categoryName;
    private String categoryURL;
    private int ImageCount;

    protected RecyclerView mRecyclerView;
    protected RecyclerView.LayoutManager mLayoutManager;

    protected static ImageAdapter mAdapter;

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(mContext, "Clicked:" + String.valueOf(position), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle args = getArguments();

        this.categoryName = args.getString("category");
        this.categoryURL = args.getString("URL");
        this.ImageCount = args.getInt("Count");

        setRetainInstance(true);
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.images_recycler_view, container, false);
        rootView.setTag(IMAGES_FRAGMENT_TAG);

        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.imagesRecyclerView);

        mRecyclerView.addItemDecoration(new CategoryItemDecoration(px, SPAN_COUNT, mCurrentLayoutType));

        mLayoutManager = new GridLayoutManager(getActivity().getApplicationContext(), SPAN_COUNT);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new ImageAdapter(categoryURL, this.ImageCount, this);
        mRecyclerView.setAdapter(mAdapter);

        return rootView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString(IMAGES_FRAGMENT_TAG, IMAGES_FRAGMENT_TAG);
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        System.out.println("OnDestroyView");
        if (mRecyclerView != null) {
            mRecyclerView.setAdapter(null);
        }
    }


    @Override
    public void onDestroy() {
        super.onDestroy();

        RefWatcher refWatcher = MyApplication.getRefWatcher(getActivity());
        refWatcher.watch(this);
    }
}

我的Adapter

public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImagesViewHolder> {

    private static final String IMAGES_FRAGMENT_TAG = "ImagesFragment";
    private Context mContext;
    private static OnItemClickListener onItemClickListener;

    private Integer imageCount;

    public ImageAdapter(String url, Integer imageCount, OnItemClickListener itemListener) {
        this.ROOTURL = url;
        this.imageCount = imageCount;
        this.onItemClickListener = itemListener;
    }

    @Override
    public int getItemCount() {
        return imageCount;
    }

    @Override
    public ImagesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        this.mContext = parent.getContext();

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.images_viewitem, parent, false);
        ImagesViewHolder imagesViewHolder = new ImagesViewHolder(v);
        return imagesViewHolder;
    }

    @Override
    public void onBindViewHolder(final ImagesViewHolder holder, int position) {
        Glide.with(mContext)
                .load("URL")
                .asBitmap()
                .centerCrop()
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .into(new BitmapImageViewTarget(holder.image) {
                    @Override
                    public void onLoadFailed(Exception e, Drawable errorDrawable) {
                        super.onLoadFailed(e, errorDrawable);
                        Log.e(IMAGES_FRAGMENT_TAG, "on load failed");
                    }

                    @Override
                    public void onResourceReady(Bitmap bitmap, GlideAnimation<? super Bitmap> glideAnimation) {
                        super.onResourceReady(bitmap, glideAnimation);
                        holder.image.setImageBitmap(bitmap);

                    }
                });
    }

    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }

    public static class ImagesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private static final int PALETTE_SIZE = 24;
        CardView cv;
        ImageView image;
        RelativeLayout mImageViewWrapper;

        ImagesViewHolder(View itemView) {
            super(itemView);
            cv = (CardView) itemView.findViewById(R.id.imagesCardView);
            image = (ImageView) itemView.findViewById(R.id.images_ImageView);
            mImageViewWrapper = (RelativeLayout) itemView.findViewById(R.id.Images_imageViewWrapper);

            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            onItemClickListener.onItemClick(v, this.getLayoutPosition());
        }
    }
}

1 个答案:

答案 0 :(得分:1)

您的问题出现在Fragment

protected static ImageAdapter mAdapter;

此行Adapter

private static OnItemClickListener onItemClickListener;

从不使用static作为变量。除非你真的知道自己在做什么,否则不要这样做。 static关键字导致ImageAdapterOnClickListener在垃圾收集后Fragment停留。这意味着变量mAdapter不是Fragment的任何实例的一部分,而是类本身的一部分 - 这当然是瞬间内存泄漏!删除那些,你应该没事。

顺便说一下,你可以很快就自己想出来。再看看LeakCanary输出:

它表示onItemClickListener中的静态变量ImageAdapter泄漏了ImagesFragment个实例 - 换句话说就是我在这个答案中告诉你的内容。

另外,您应该阅读this answer以了解有关内存泄漏的更多信息。