首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >FragmentManager(v4)未从mCreatedMenus中删除碎片

FragmentManager(v4)未从mCreatedMenus中删除碎片
EN

Stack Overflow用户
提问于 2017-06-21 19:20:57
回答 3查看 1.7K关注 0票数 9

LeakCanary在我的代码中发现了漏洞

代码语言:javascript
复制
* 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什么时候被洗脱的。我发现的唯一代码是当新的片段被添加时。难道不应该以某种方式管理它吗?

代码语言:javascript
复制
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;
    }
EN

回答 3

Stack Overflow用户

发布于 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修复片段管理器以从该列表中删除泄漏的片段之前,一些选项包括:

  • 现场直播了泄露的片段。当活动被销毁时,片段管理器将被清除,然后片段将被GC认领。
  • 不要使用setHasOptionsMenu()机制。例如,您可以提出自己的菜单implemenation.
  • Use反射来从该列表中删除片段。反射当然不是理想的,但泄漏碎片要糟糕得多。在其他泄漏的片段中,添加类似以下

的内容

代码语言:javascript
复制
@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。

票数 7
EN

Stack Overflow用户

发布于 2018-08-10 02:09:31

这是Android SDK中的一个漏洞。看看this thread吧。

如果您在gradle应用程序文件(build.gradle)中更新为Target- support -26.0.0-beta1 support lib,则此问题已修复。

如果由于某些原因您无法更新到supportLibVersion>=26-beta1,,则有一种解决方法:

代码语言:javascript
复制
public class FragmentUtils { 

    /** 
     * Hack to force update the LoaderManager's host to avoid a memory leak in retained/detached fragments. 
     * Call this in {@link Fragment#onAttach(Activity)} 
     */ 
    public static void updateLoaderManagerHostController(Fragment fragment) { 
        if (fragment.mHost != null) { 
            fragment.mHost.getLoaderManager(fragment.mWho, fragment.mLoadersStarted, false); 
        } 
    } 

    /** 
     * This hack is to prevent the root loader manager to leak previous instances of activities 
     * accross rotations. It should be called on activities using loaders directly (not via a fragments). 
     * If the activity has fragment, you also have to also {@link #updateLoaderManagerHostController(Fragment)} above 
     * for each fragment. 
     * Call this in {@link FragmentActivity#onCreate} 
     * 
     * @param activity an actvity that uses a loader and leaks on rotation. 
     */ 
    public static void updateLoaderManagerHostController(FragmentActivity activity) { 
        if (activity.mFragments != null) { 
            try { 
                final Field mHostField = activity.mFragments.getClass().getDeclaredField("mHost"); 
                mHostField.setAccessible(true); 
                FragmentHostCallback mHost = (FragmentHostCallback) mHostField.get(activity.mFragments); 
                mHost.getLoaderManager("(root)", false, true /* the 2 last params are not taken into account*/); 
            } catch (IllegalAccessException e) { 
                e.printStackTrace(); 
            } catch (NoSuchFieldException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 
}
票数 0
EN

Stack Overflow用户

发布于 2019-01-09 04:30:40

我对androidX FragmentManager有这个问题。

已通过在片段事务后调用此方法修复泄漏。此方法位于活动内部。

代码语言:javascript
复制
    private fun clearFragmentManagersAddedMenus() {
        Handler(mainLooper).post {
            val field = FragmentActivity::class.java.getDeclaredField("mFragments")
            field.isAccessible = true
            (field.get(this) as FragmentController).dispatchCreateOptionsMenu(null, null)
        }
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44674696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档