Android 保存 Fragment 引用及 getActivity() 为空问题
- UID
- 1066743
|
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() 来创建它. |
|
|
|
|
|