java - 使用onClick接口(interface)时RecyclerView适配器导致内存泄漏

标签 java android memory-leaks android-recyclerview

在我的RecyclerView.Adapter中我定义了这个接口(interface):

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

包含RecyclerView的Fragment实现了这个接口(interface)。 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);
    }
}

我的适配器:

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

最佳答案

您的问题是您的Fragment中的这一行:

protected static ImageAdapter mAdapter;

适配器中的这一行:

private static OnItemClickListener onItemClickListener;

永远不要使用static作为变量。除非你真的知道自己在做什么,否则就不要这样做。 static 关键字会导致 ImageAdapterOnClickListenerFragment 被垃圾回收后保留下来。这意味着变量 mAdapter 不是 Fragment 任何实例的一部分,而是类本身的一部分 - 这当然是即时内存泄漏!删除这些,你应该没问题。


顺便说一句,您自己可以很快地弄清楚这一点。再看一下 LeakCanary 的输出:

它说 ImageAdapter 中的静态变量 onItemClickListener 泄漏了 ImagesFragment 实例 - 或者换句话说,正是我在中告诉你的这个答案。


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

关于java - 使用onClick接口(interface)时RecyclerView适配器导致内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36060804/

相关文章:

java - LinearLayout 中的中心按钮

安卓 : How to get whether Bluetooth Device PC or Mobile?

android - 通过外部广播接收器重启应用程序

c++ - Windows 堆分配调用堆栈 - 奇怪的调用堆栈

android - 位图大小超出了 Android 上的 VM 预算错误

java - 无法让所有UI组件出现在RelativeLayout中

java - 未应用自定义 Spring Boot 安全性

javascript - 在 Dojo 类中递归调用 setTimeout 时是否存在内存泄漏?

java - 方法返回负值

java - 公共(public)静态类的 YAJSW 错误