Fragment 嵌套 Fragment 时正确的获取 FragmentManager

阅读 4528
收藏 133
2016-08-29
原文链接:blog.luolc.com

在使用了Google MVP architecture之后,View层以一个动态Fragment的形式独立于Activity。在原本使用Activity作为View层时,如果需要内嵌多个动态Fragment,例如常见的ViewPager,我们可以使用Activity的FragmentManager来管理他们。而在Google MVP模式下,这就变成了一个View层的主Fragment嵌套许多子Fragment。在这时,我们应相应的用View层Fragment的FragmentManager来管理子Fragment。而这时,获取FragmentManager的方法应为getChildFragmentManager(),注意这里容易写错成getActivity().getSupportFragmentManager(),如果使用后者,则获得的是外层Activity的FragmentManager,此时是不符合逻辑的。

例如PKU Helper 3.0+版本的树洞页面,HoleFragment为整体的View层,其中只包含一个ViewPager。ViewPager包含树洞关注两个HoleListFragment。

树洞截图

在HoleFragment中,有如下setupPager()方法,在onCreateView()中被调用,负责初始化ViewPager。

private void setupPager() {
    List fragments = new ArrayList<>();
    if (getActivity().getSupportFragmentManager().getFragments() != null && 
            !getActivity().getSupportFragmentManager().getFragments().isEmpty()) {
        fragments = getActivity().getSupportFragmentManager().getFragments();
        mMainHolesViewFragment = (HoleListFragment) fragments.get(HoleContract.HolesView.POSITION_MAIN);
        mAttentionHolesViewFragment = (HoleListFragment) fragments.get(HoleContract.HolesView.POSITION_ATTENTION);
    }
    else {
        mMainHolesViewFragment = HoleListFragment.newInstance(HoleContract.HolesView.POSITION_MAIN);
        mAttentionHolesViewFragment = HoleListFragment.newInstance(HoleContract.HolesView.POSITION_ATTENTION);
        fragments.add(mMainHolesViewFragment);
        fragments.add(mAttentionHolesViewFragment);
    }
    mMainHolesViewFragment.setPresenter(mPresenter);
    mAttentionHolesViewFragment.setPresenter(mPresenter);

    mViewPager.setAdapter(new HolePagerAdapter(getActivity().getSupportFragmentManager(), fragments));
}

如果按以上代码编写,在重加载HoleActivity时,会弹出NullPointer异常。追根溯源,是重加载时初始化的Fragment并非是15、16行中与Presenter绑定的Fragment,即他们是不同的实例,这在debug模式中查看内存地址可以得到。于是在Presenter调用View层,即子Fragment中的方法时,调用的是未初始化的Fragment,其又含有未赋值的成员变量,故在调用这些成员变量时,触发了NullPointer异常。

正确的方法是,将上述代码中全部的getActivity().getSupportFragmentManager()替换为getChildFragmentManager(),这时我们才获取的是正确的FragmentManager。

抽象图

在上图中我们可以更加清晰的看到,负责管理ViewPager中的两个子Fragment的应当是FragmentManager#2,所以用getChildFragmentManager()才是正确的做法。

评论