LeakCanary在我的代码中发现了漏洞
* classifieds.yalla.features.ad.page.seller.SellerAdPageFragment has leaked:
* GC ROOT android.view.inputmethod.InputMethodManager$1.this$0 (anonymous subclass of com.android.internal.view.IInputMethodClient$Stub)
* references android.view.inputmethod.InputMethodManager.mNextServedView
* references android.support.v4.widget.DrawerLayout.mContext
* references classifieds.yalla.features.host.HostActivity.fragNavController
* references com.ncapdevi.fragnav.FragNavController.mFragmentManager
* references android.support.v4.app.FragmentManagerImpl.mCreatedMenus
* references java.util.ArrayList.elementData
* references array java.lang.Object[].[0]
* leaks classifieds.yalla.features.ad.page.seller.SellerAdPageFragment instance但当我查看FragmentManagerImpl
我没发现FragmentManagerImpl.mCreatedMenus什么时候被洗脱的。我发现的唯一代码是当新的片段被添加时。难道不应该以某种方式管理它吗?
public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        boolean show = false;
        ArrayList<Fragment> newMenus = null;
        if (mAdded != null) {
            for (int i=0; i<mAdded.size(); i++) {
                Fragment f = mAdded.get(i);
                if (f != null) {
                    if (f.performCreateOptionsMenu(menu, inflater)) {
                        show = true;
                        if (newMenus == null) {
                            newMenus = new ArrayList<Fragment>();
                        }
                        newMenus.add(f);
                    }
                }
            }
        }
        if (mCreatedMenus != null) {
            for (int i=0; i<mCreatedMenus.size(); i++) {
                Fragment f = mCreatedMenus.get(i);
                if (newMenus == null || !newMenus.contains(f)) {
                    f.onDestroyOptionsMenu();
                }
            }
        }
        mCreatedMenus = newMenus;
        return show;
    }发布于 2019-11-18 00:08:51
这个问题现在在androidx.fragment v1.10 (2019年11月)上仍然相关,所以这里有一些关于它的见解。
假设使用片段f的真值调用setHasOptionsMenu()。当f被分离时,与f关联的片段管理器(FM)将不会处理菜单上隐含的更改。请记住,菜单可能会受到同一FM托管的多个片段的影响。它们中的一个f被分离的事实本应导致FM重新构建菜单,但话又说回来,这并没有得到处理。此外,当f被分离时,在支持菜单的上下文中与f相关联的资源也不会被清除。特别是,不会在f上调用onDestroyOptionsMenu(),FM会在提供菜单选项的片段列表中保留对f的引用。
在Google修复片段管理器以从该列表中删除泄漏的片段之前,一些选项包括:
的内容
@Override
public void onDetach() {
    super.onDetach();
    // get the fragment manager associated with this fragment
    FragmentManager fragmentManager = getFragmentManager();
    if (fragmentManager != null) {
        try {
            Field field = 
                fragmentManager.getClass().getDeclaredField("mCreatedMenus");
            field.setAccessible(true);
            if (field.get(fragmentManager) instanceof ArrayList) {
                ArrayList fragments = (ArrayList)field.get(fragmentManager);
                if (fragments != null && fragments.remove(this)) {
                    Log.d(TAG, "Yay, no leak today");
                }
            }
        } catch (NoSuchFieldException | SecurityException | 
                 IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}注意:当片段相关的代码改变时,这个解决方案自然是脆弱的,然而,这是可测试的。此外,如果使用proguard,则需要确保避免对该字段进行混淆,因此可以像这样添加proguard指令:
-keep class androidx.fragment.app.FragmentManagerImpl { *; }
或者更好的是,试着弄清楚如何使用-keepclassmembers来保持mCreatedMenus。
https://stackoverflow.com/questions/44674696
复制相似问题