首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Android 保存 Fragment 引用及 getActivity() 为空问题

Android 保存 Fragment 引用及 getActivity() 为空问题

问题

做 Android 应用开发的小伙伴们大多都被 Fragment 坑过. 最近研究了其中常见的一种坑, 记录下来, 以免遗忘. 问题大体是这样的:
有时我们希望在 Activity 中保存所创建的 Fragment 的引用, 以便后续逻辑中做界面更新等操作. 如果页面中的 Fragment 都是静态的 (不会被 remove, hide 等), 则一般不会出啥问题. 如果是多个 Fragment 切换的场景, 就容易出现 getActivity() 为 null 等问题. 这种问题在使用 FragmentPagerAdapter 时尤其容易出现.
这里涉及两个问题: Fragment 的创建和 Fragment 引用的保存. 两个问题都有坑.

先放结论 (编程建议):

    不要在 Activity.onCreate() 中直接 new Fragment(). Fragment 的创建应尽量纳入 FragmentManager 的管理.
    尽量不要保存 Fragment 的引用. 在需要直接调用 Fragment 时, 使用 FragmentManager.findFragmentByTag() 等方法获取相关 Fragment 的引用.
    如果一定要保存 Fragment 引用, 则要谨慎选择获取引用的节点.

原因分析

以一段实际代码说明.
遇到主页需要左右滑动切换标签页的需求, 最常用的就是 ViewPager + FragmePagerAdapter 方案了. 很多小伙伴可能会这样写 (示例代码1):

    public class TabChangeActivity extends AppCompatActivity {
     
        private ArrayList<Fragment> mFragmentList;
        private ViewPager mViewPager;
     
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_tab_fragment_sample);
            mFragmentList = new ArrayList<>(3);
            mFragmentList.add(new Fragment1());
            mFragmentList.add(new Fragment2());
            mFragmentList.add(new Fragment3());
            mViewPager = (ViewPager) findViewById(R.id.view_pager);
            mViewPager.setAdapter(new SlidePagerAdapter(getSupportFragmentManager()));
        }
     
        private class SlidePagerAdapter extends FragmentPagerAdapter {
     
            public SlidePagerAdapter(FragmentManager fm) {
                super(fm);
            }
     
            @Override
            public Fragment getItem(int position) {
                return mFragmentList.get(position);
            }
     
            @Override
            public int getCount() {
                return mFragmentList.size();
            }
        }
    }

上例是一个最简单的标签页切换界面写法, 布局中只有一个 ViewPager, 就不再贴出了.
但这段代码是存在隐患的.
这里首先复习一下 Activity 管理 Fragment 的方式. 在代码中动态显示 Fragment 时, 大体流程如下:

    private void showFragment1() {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 查看 fragment1 是否已经被添加
        Fragment1 fragment1 = (Fragment1) fragmentManager.findFragmentByTag("fragment1");
        if (fragment1 == null) {
            // fragment1 尚未被添加, 则创建并添加
            fragment1 = new Fragment1();
            transaction.add(R.id.submitter_fragment_container, fragment1, "fragment1");
        } else {
            // fragment1 已被添加, 则调用 show() 方法让其显示
            transaction.show(fragment1);
        }
        transaction.commit();
    }

但 示例代码1 中并没有类似逻辑. 其实是被 FragmentPagerAdapter 封装了, 但逻辑依然是一样的:
FragmentPagerAdapter 在需要展示 fragment1 时, 会首先尝试通过 FragmentManager.findFragmentByTag() 找到它. 如果找不到, 才会调用 FragmentPagerAdapter.getItem() 来创建它.
返回列表