在方向更改

时间:2015-05-08 20:50:15

标签: android listview android-recyclerview

我曾经在我的Android应用程序中使用ListView,我最近切换到RecyclerView并观察到它在方向更改时引入了一些内存泄漏。经过进一步调查,原因显而易见

SETUP

单个activity,其中包含fragment,其实例在配置更改时保留。 fragment在其布局文件中包含一个RecyclerView,该文件使用自定义adapter填充

钻井

每当为这两个视图中的任何一个设置Adapter时,它们就会使用适配器注册自己,以监视数据的更改并在UI上进行更新。 ListView通过

取消注册配置更改
@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    ...

    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
        mDataSetObserver = null;
    }

    ...
}

不幸的是,RecyclerView没有这样做

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (mItemAnimator != null) {
        mItemAnimator.endAnimations();
    }
    mFirstLayoutComplete = false;

    stopScroll();
    mIsAttached = false;
    if (mLayout != null) {
        mLayout.onDetachedFromWindow(this, mRecycler);
    }
    removeCallbacks(mItemAnimatorRunner);
}

PROOF

我更改了方向很多次,然后进行了堆转储,并使用Eclipse的MAT读取它。我确实看到我的活动有很多实例,因为RecyclerView实例没有unregister,而且他们对我的活动有很强的参考!!

我错过了什么吗?你们如何确保RecyclerView不泄漏你的活动?

FRAGMENT

public class ExampleFragment extends Fragment {

    private ExampleAdapter mAdapter = null;

    public static ExampleFragment newInstance() {
        return new ExampleFragment();
    }


    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        setupAdapterIfRequired();
    }

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_example, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setupRecyclerView(getView());
    }

    private void setupAdapterIfRequired() {
        if (mAdapter == null) {
            mAdapter = new ExampleAdapter();
        }
    }

    private void setupRecyclerView(View rootView) {
        RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.list);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
        recyclerView.setAdapter(mAdapter);
    }
}

3 个答案:

答案 0 :(得分:20)

将此添加到Fragment为我阻止泄漏:

@Override
public void onDestroyView() {
    super.onDestroyView();
    recyclerView.setAdapter(null);
}

答案 1 :(得分:3)

这不是RecyclerView的问题。这是因为您正在设置setRetainInstance(true)

setRetainInstance()只能与没有视图的片段一起使用,否则会导致内存泄漏。

当方向更改活动被终止时,但片段中的视图仍然使用来自该活动的上下文。这就是你看到内存泄漏的原因。

答案 2 :(得分:0)

我遇到了同样的问题。 我做了一些搜索,但没有找到关于这个帖子的任何内容。

所以我问自己:为什么我需要在RecyclerView.Adapter中使用上下文? (在ArrayAdapter中,您使用它来渗透某些布局)但是在这个新模式中,您可以使用父上下文来扩充布局:

e.g。

define([
    'backbone'
], function(Backbone) {

    var AppState = Backbone.Model.extend({

        ...

    });

    // Return as a singleton (instantiated).
    return new AppState();
});

与绑定相同

public class RecyclerViewAdapter extends RecyclerView.Adapter<VH> {
     @Override
     public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
          return LayoutInflater.from(parent.getContext()).inflate(R.layout.item_action, parent, false);
     }
}

希望有所帮助