如何在使用 recyclerview 分离 Viewpager 中的视图之前分离父级?

How to detach parent before detaching the views in Viewpager with recyclerview?

由于我的 recyclerview,我遇到了内存泄漏问题,它在 LeakCanary 中说 View detached and has parent。我试图在 onDestroyView 上将 recyclerview 设置为空,但仍然没有任何反应。下面是我的片段和 LeakCanary 的堆栈跟踪:

public class CancelledOrdersFragment extends Fragment {
    private RecyclerView recyclerView;
    private FirebaseFirestore db = FirebaseFirestore.getInstance();
    private CancelledOrdersAdapter adapter;
    private OrderTransactionsModel model;

    public CancelledOrdersFragment() {
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View itemView = inflater.inflate(R.layout.fragment_cancelled_orders, container, false);
        recyclerView = itemView.findViewById(R.id.cancelled_orders_rv);

        LinearLayoutManager linearLayoutManager1 = new LinearLayoutManager(itemView.getContext());
        linearLayoutManager1.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(linearLayoutManager1);

        Paper.init(itemView.getContext());
        String storeID = Paper.book().read("userid");

        Query query = db.collection("Transactions").whereEqualTo("storeID", storeID).whereEqualTo("transactionStatus", "cancelled").orderBy("orderTimeout", Query.Direction.DESCENDING);

        PagedList.Config Bconfig = new PagedList.Config.Builder()
                .setEnablePlaceholders(false)
                .setPrefetchDistance(10)
                .setPageSize(8)
                .build();

        FirestorePagingOptions<OrderTransactionsModel> options = new FirestorePagingOptions.Builder<OrderTransactionsModel>()
                .setLifecycleOwner(getActivity())
                .setQuery(query, Bconfig, snapshot -> {
                    OrderTransactionsModel transactionsModel = snapshot.toObject(OrderTransactionsModel.class);
                    String transID = transactionsModel.getTransactionID();
                    String tranCOde = transactionsModel.getTransactionCode();
                    double storeTotal = transactionsModel.getStoreTotal();
                    HashMap<String, Object> CouponUsed = transactionsModel.getCouponUsed();
                    HashMap<String, Date> OrderStatus = transactionsModel.getOrderStatus();

                    model = new OrderTransactionsModel(transID, storeID, storeTotal, CouponUsed, OrderStatus, tranCOde);
                    return model;
                })
                .build();

        adapter = new CancelledOrdersAdapter(options);
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();

        return itemView;
    }


    @Override
    public void onDestroyView() {
        super.onDestroyView();
        adapter.stopListening();
        recyclerView = null;
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
        adapter.startListening();
    }
 ┬───
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │ GC Root: Local variable in native code
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ android.net.ConnectivityThread instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: NO (PathClassLoader↓ is not leaking)
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Thread name: 'ConnectivityThread'
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ ConnectivityThread.contextClassLoader
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ dalvik.system.PathClassLoader instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: NO (InternalLeakCanary↓ is not leaking and A ClassLoader is never leaking)
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ PathClassLoader.runtimeInternalObjects
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ java.lang.Object[] array
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: NO (InternalLeakCanary↓ is not leaking)
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ Object[].[1614]
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ leakcanary.internal.InternalLeakCanary class
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: NO (OrdersActivity↓ is not leaking and a class is never leaking)
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ static InternalLeakCanary.resumedActivity
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ com.dreamakers.clustore.clustorestore.Activity.OrdersActivity instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: NO (Activity#mDestroyed is false)
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ OrdersActivity.mLifecycleRegistry
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                     ~~~~~~~~~~~~~~~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ androidx.lifecycle.LifecycleRegistry instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ LifecycleRegistry.mObserverMap
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                        ~~~~~~~~~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ androidx.arch.core.internal.FastSafeIterableMap instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ FastSafeIterableMap.mEnd
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                          ~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ androidx.arch.core.internal.SafeIterableMap$Entry instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ SafeIterableMap$Entry.mKey
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                            ~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ com.dreamakers.clustore.clustorestore.Adapter.CancelledOrdersAdapter instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ CancelledOrdersAdapter.mObservable
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                             ~~~~~~~~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ androidx.recyclerview.widget.RecyclerView$AdapterDataObservable instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ RecyclerView$AdapterDataObservable.mObservers
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                                         ~~~~~~~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ java.util.ArrayList instance
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ ArrayList.elementData
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                ~~~~~~~~~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ java.lang.Object[] array
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ Object[].[0]
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │               ~~~
2020-08-19 16:23:59.404 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ androidx.recyclerview.widget.RecyclerView$RecyclerViewDataObserver instance
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: UNKNOWN
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ RecyclerView$RecyclerViewDataObserver.this[=11=]
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │                                            ~~~~~~
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ├─ androidx.recyclerview.widget.RecyclerView instance
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    Leaking: YES (View detached and has parent)
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    mContext instance of com.dreamakers.clustore.clustorestore.Activity.OrdersActivity with mDestroyed = false
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    View#mParent is set
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    View#mAttachInfo is null (view detached)
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    View.mID = R.id.cancelled_orders_rv
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    View.mWindowAttachCount = 1
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: │    ↓ RecyclerView.mParent
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ╰→ android.widget.FrameLayout instance
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     Leaking: YES (ObjectWatcher was watching this because com.dreamakers.clustore.clustorestore.Activity.CancelledOrdersFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     key = 1bb796b4-4c87-4320-b90a-a348a14e616e
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     watchDurationMillis = 8633
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     retainedDurationMillis = 3631
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     mContext instance of com.dreamakers.clustore.clustorestore.Activity.OrdersActivity with mDestroyed = false
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     View#mParent is null
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     View#mAttachInfo is null (view detached)
2020-08-19 16:23:59.405 11647-13298/com.dreamakers.clustore.clustorestore D/LeakCanary: ​     View.mWindowAttachCount = 1

leaktrace 显示 OrdersActivity 还活着(没有被破坏,一切都很好)并且它的生命周期注册表有一个包含 CancelledOrdersAdapter 的 mObserverMap。

根据代码,CancelledOrdersAdapter 和 R.id.cancelled_orders_rv RecyclerView 的生命周期似乎应该保持一致。

但是,当回收器视图分离时,CancelledOrdersAdapter 会保留在内存中,因为它注册为 activity 生命周期的观察者(此时未被销毁)。该修复程序可能会将 CancelledOrdersAdapter 注册为片段视图生命周期的观察者,即 Fragment.viewLifecycleOwner