S
S
Sergey Semenko2017-03-12 14:38:34
Android
Sergey Semenko, 2017-03-12 14:38:34

Android. How to deal with such memory leaks?

LeakCanary reports leaks in click handlers:

@Override
    protected void setupAdapter(CatalogAdapter adapter) {
        adapter.setOnItemClickListener(new BaseAdapter.OnItemClickListener<Item>()
        {
            @Override
            public void onClick(View view, Item item, int position) {
                // Утечка, как я понял, происходит из-за getContext(), даже если никаких нажатий не происходит. Возможно я ошибаюсь и проблема где-то в другом месте.
                Intent intent = new Intent(getContext(), ShowActivity.class);
                intent.putExtra(KEY_ITEM, Parcels.wrap(item));
                startActivity(intent);
            }
        });
    }

This method is called in onCreateView :
@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutResource(), container, false);
        ...
        setupAdapter(getAdapter());
        ...
        return view;
    }

The adapter is created in onCreate:
@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        ...
        adapter = createAdapter();
        ...
    }

BaseAdapter code:
public abstract class BaseAdapter<T, VH extends RecyclerView.ViewHolder> extends PaginationAdapter<T, VH>
{
    private List<T> items;
    private Context context;

    private OnItemClickListener<T> onItemClickListener;

    public BaseAdapter(Context ctx) {
        items = new ArrayList<>();
        context = ctx.getApplicationContext();
    }

    @Override
    public int getItemCount() {
        return items.size();
    }
    
    public T getItem(int position) {
        return items.get(position);
    }

    public void setOnItemClickListener(OnItemClickListener<T> onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    protected void callOnClick(View view, T item, int position) {
        if (onItemClickListener != null) {
            onItemClickListener.onClick(view, item, position);
        }
    }

    protected LayoutInflater getLayoutInflater(View view) {
        return LayoutInflater.from(view.getContext());
    }

    protected Context getContext() {
        return context;
    }

    public interface OnItemClickListener<T>
    {
        void onClick(View view, T item, int position);
    }
}

Here is the information obtained by LeakCanary:
54b165a0fd1c46089c0a4c45118cb2fd.png

Answer the question

In order to leave comments, you need to log in

2 answer(s)
D
DocentTSR, 2017-03-17
@abler98

Sergey,
1. Fragments with setRetainInstance(true) set must not have an interface/view at all! This is written in the documentation, in black and white. This is your leak.
additionally: And you can’t put it in the backstack either!
2. any anonymous/internal non-static class - has a non-explicit reference to the external one!
3.
- you don't need to do anything special in the onCreate method of the fragment.
- in the onCreateView method, you need to create a current view, well, or even such small operations as setHasOptionsMenu.
- all the main logic is done in onViewCreated, after the fragment has been attached and created all the views.
4. for the future, you can reset any listener in the
onDestroy - Activity method
onDestroyView - Fragment
for example
adapter.setOnItemClickListener(null)
and it is important to do this BEFORE the super method!
5. From experience, all these LeakCanaries are for lazy hipsters.
A normal tool that will tell you exactly what is flowing, how much, and most importantly, WHO is holding this link will show - MAT (Memory Analyzer Tool) in conjunction with DDMS, do not be too lazy to figure it out, there are a lot of articles on Habré.

D
DJOxid, 2017-03-16
@DJOxid

You can call view.getContext().
And so the reference to the external object is saved, for which getContext () is called.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question