RecyclerView leaks when recreating

My RecyclerView creates a memory leak every time its activity is recreated. I have google but I was able to find any solution for this. The activity is destroyed by using the Back button and creating it by clicking the My Main Activity button. I also posted the canary leak result. Thank.

//CommentListAdapater
package com.support.android.designlibdemo.adapters;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.squareup.otto.Subscribe;
import com.support.android.designlibdemo.MyApplication;
import com.support.android.designlibdemo.R;
import com.support.android.designlibdemo.events.CommentsLoadEvent;
import com.support.android.designlibdemo.events.EventFinished;
import com.support.android.designlibdemo.events.NetworkErrorEvent;
import com.support.android.designlibdemo.models.RedditComment;
import com.support.android.designlibdemo.models.RedditObject;
import com.support.android.designlibdemo.models.RedditResponse;
import com.support.android.designlibdemo.models.RedditThread;
import com.support.android.designlibdemo.services.RedditService;

import java.util.ArrayList;
import java.util.List;

import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;

public class CommentListAdapter extends RecyclerView.Adapter<CommentListAdapter.ViewHolder> {
    private List<RedditComment> mValues = new ArrayList();
    private Context mContext;
    private static final String SUBREDDIT_NAME = "subreddit_name";
    private static final String THREAD_ID = "thread_id";
    private static final String THREAD_NAME = "thread_name";

    public CommentListAdapter(Context context) {
        mContext = context;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public View mView;
        public TextView mBody;
        public TextView mAuthor;

        public ViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
            mBody = (TextView) itemView.findViewById(R.id.body);
            mAuthor = (TextView) itemView.findViewById(R.id.author_name);
        }
    }

    @Override
    public void onBindViewHolder(CommentListAdapter.ViewHolder holder, int position) {
        if (mValues.get(position).selftext == null) {
            holder.mBody.setText(mValues.get(position).body);
        } else {
            holder.mBody.setText(mValues.get(position).selftext);
        }
        holder.mAuthor.setText(mValues.get(position).author);
    }

    @Override
    public int getItemCount() {
        if (mValues == null) {
            return 0;
        }
        return mValues.size();
    }

    @Override
    public CommentListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.activity_comment_holder, parent, false);

        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Subscribe
    public void fetchData(final CommentsLoadEvent event) {
        RedditService.Implementation.get().getThreadComments(event.subredditName, event.threadId,
                new Callback<List<RedditResponse<RedditComment>>>() {
                    @Override
                    public void success(List<RedditResponse<RedditComment>> redditResponses, Response response) {
                        mValues.clear();
                        for (RedditResponse rr : redditResponses) {
                            RedditComment rc = (RedditComment) rr.getData();
                            for (RedditObject obj : rc.children) {
                                if (obj instanceof RedditThread) {
                                    RedditThread t = (RedditThread) obj;
                                    RedditComment c = new RedditComment();
                                    c.selftext = t.selftext;
                                    c.author = t.author;
                                    mValues.add(c);
                                } else {
                                    RedditComment c = (RedditComment) obj;
                                    mValues.add(c);
                                }
                                notifyDataSetChanged();
                                MyApplication.getBus().post(new EventFinished());
                            }
                        }
                    }

                    @Override
                    public void failure(RetrofitError error) {
                        MyApplication.getBus().post(new NetworkErrorEvent(error, mContext));
                    }
                }
        );

    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        MyApplication.getBus().register(this);
    }

    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        MyApplication.getBus().unregister(this);
    }
}

//CommentActivity
package com.support.android.designlibdemo.activities;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;

import com.squareup.otto.Subscribe;
import com.support.android.designlibdemo.MyApplication;
import com.support.android.designlibdemo.R;
import com.support.android.designlibdemo.adapters.CommentListAdapter;
import com.support.android.designlibdemo.events.CommentsLoadEvent;
import com.support.android.designlibdemo.events.EventFinished;

public class CommentActivity extends BaseActivity {
    public static final String SUBREDDIT_NAME = "subreddit_name";
    public static final String THREAD_ID = "thread_id";
    public static final String THREAD_NAME = "thread_name";

    private final String TAG = getClass().getSimpleName();
    private RecyclerView mRecyclerView;
    private SwipeRefreshLayout mSwipeRefreshLayout;
    private CommentListAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private String mSubredditName;
    private String mThreadName;
    private String mThreadId;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        Intent intent = getIntent();
        mSubredditName = intent.getStringExtra(SUBREDDIT_NAME);
        mThreadName = intent.getStringExtra(THREAD_NAME);
        mThreadId = intent.getStringExtra(THREAD_ID);

        toolbar.setSubtitle(mThreadName);
        toolbar.setTitle(mSubredditName);
        setToolbar();
        setupRecyclerView();
    }

    @Override
    protected boolean hasCustomIcon() {
        return false;
    }

    @Override
    protected int getLayout() {
        return R.layout.activity_comment;
    }

    private void setupRecyclerView() {
        if (mRecyclerView == null) {

            mRecyclerView = (RecyclerView) findViewById(R.id.comment_recycler);
            mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
        }

        if (mLayoutManager == null) {
            // use a linear layout manager
            mLayoutManager = new LinearLayoutManager(this);
            mRecyclerView.setLayoutManager(mLayoutManager);
            mSwipeRefreshLayout.setColorSchemeColors(R.color.colorAccent,
                                                     R.color.color_primary_dark,
                                                     R.color.frame_background);
            mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    MyApplication.getBus().post(new CommentsLoadEvent(
                                    mSubredditName, mThreadId)
                    );
                }
            });
        }

        if (mAdapter == null) {
            mAdapter = new CommentListAdapter(this);
            mRecyclerView.setAdapter(mAdapter);
            mSwipeRefreshLayout.setRefreshing(true);
            MyApplication.getBus().post(new CommentsLoadEvent(
                            mSubredditName, mThreadId)
            );
        }
    }

    @Subscribe
    public void stopSwipe(final EventFinished event) {
        mSwipeRefreshLayout.setRefreshing(false);
    }

    @Override
    public void onPause() {
        super.onPause();
        MyApplication.getBus().unregister(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        MyApplication.getBus().register(this);
    }
}

//MyApplication
package com.support.android.designlibdemo;

import android.app.Application;

import com.crashlytics.android.Crashlytics;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.otto.Bus;

import io.fabric.sdk.android.Fabric;

public class MyApplication extends Application {
    private static Bus _bus;

    @Override public void onCreate() {
        super.onCreate();
        Fabric.with(this, new Crashlytics());
        LeakCanary.install(this);
    }

    public static Bus getBus() {
        if (_bus == null) {
            _bus = new Bus();
        }
        return _bus;
    }
}

      

leak image 1

leak image 2

+3


source to share


2 answers


private static Bus _bus;

This is saving instances from release. CommentListAdapter accesses this field where you have an instance in CommentActivity. This makes the CommentActivity impossible to release (leak).

I am not familiar with Otto, but you cannot use him correctly.



If you want to communicate with the activity on the adapter, why don't you make a public method in the adapter and call the .method () method?

+1


source


When yours Activity

is destroyed, your adapter will not be automatically disconnected from RecyclerView

, so the adapter will still be registered with Otto and will leak. You will need to manually unregister your adapter if it Activity

is destroyed.



+1


source







All Articles