在方向更改时,RecyclerView不会从适配器取消注册

x-t*_*eme 16 android listview android-recyclerview

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

建立

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

钻井

只要Adapter为这两个视图中的任何一个设置了一个视图,它们就会使用适配器注册自己,以监视数据的更改并在UI上进行更新.ListView取消注册配置更改

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    ...

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,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);
}
Run Code Online (Sandbox Code Playgroud)

证明

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

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

分段

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

InT*_*nds 23

将此添加到Fragment我的已停止泄漏:

@Override
public void onDestroyView() {
    super.onDestroyView();
    recyclerView.setAdapter(null);
}
Run Code Online (Sandbox Code Playgroud)

  • 知道为什么这个工作或更好但为什么recyclerview不会自动释放适配器参考? (4认同)

Pon*_*pat 4

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

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

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

  • `当方向改变时,活动将被终止,但片段中的视图仍使用该活动的上下文。这就是为什么你会看到内存泄漏。` - 这不是我的内存泄漏的原因。在方向更改时,将重新创建“fragment”的视图(因此会丢失活动的引用 - 作为上下文),但我用来填充“RecyclerView”的适配器将保留对该视图的强引用,因为它没有取消注册哪个视图反过来将持有对活动本身的强引用,从而导致泄漏 (2认同)
  • 但正如你所说,可以通过不保留包含这些“RecyclerView”的片段来防止泄漏 (2认同)
  • 当 setRetainedInstance 设置为 true 时,Fragment 的实例将在方向更改或活动重新创建时保留。然而,onDestroyView() 仍然被调用,并且视图层次结构应该被垃圾收集。OP 的问题是他们的视图层次结构被泄露。这意味着必须存在对未销毁的视图的引用。OP是正确的,该引用是通过适配器而不是片段保存的。从 RecyclerView 中分离适配器可以解决问题。 (2认同)