Android 保存 Fragment 引用及 getActivity() 为空问题(2)
- UID
- 1066743
|
Android 保存 Fragment 引用及 getActivity() 为空问题(2)
回到 示例代码1, 在正常情况下, 这段代码是可以完美运行的. 但如果我们的界面被系统回收掉了, 当用户再次返回这个界面时, 问题就来了. 在这种情况下:
因为 Activity 被销毁了, 因此 onCreate() 会被调用, 我们的三个 Fragment 会被重新创建并装入 mFragmentList 数组.
又因为 Activity 被销毁了, 因此系统会自动恢复界面状态, 包括之前已经被添加的 Fragment. 恢复完成后, 轮到 FragmentPagerAdapter 显示 fragment1. FragmentPagerAdapter 通过 FragmentManager.findFragmentByTag(), 发现 fragment1 已经被添加了 (被添加的为老 Fragment, 即被系统恢复的那个). 因此不会再去调用 FragmentPagerAdapter.getItem(), 因此 FragmentPagerAdapter 直接显示了被系统恢复出来的 fragment1.
没错, 这种情况下, Fragment1 在 Activity 中其实有两个实例:
一个是真正的被 Activity 添加并显示的实例;
一个是在 onCreate() 中被创建, 并保存在 mFragmentList 中的没有什么卵用的实例.
可以想见, 这种状态下肯定会出现很多莫名其妙的问题, 其中就包括 getActivity() 返回 null 的问题.
吐槽: FragmentPagerAdapter.getItem() 方法明明就是 FragmentPagerAdapter 用来内部创建 Fragment 用的啊, 根本不是用来供外部获取 Fragment 用的. 如果改名叫 createItem() 或者 createFragment() 之类的, 估计可以防止不少人掉坑的.
代码修正
基于以上分析可知, 在 Activity.onCreate() 中创建 Fragment 是不恰当的. 应该把 Fragment 的创建放在 FragmentPagerAdapter.getItem() 中. 经过改进的 示例代码1 如下:
public class TabChangeActivity extends AppCompatActivity {
private ViewPager mViewPager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_fragment_sample);
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) {
switch (position) {
case 0:
return new Fragment1();
case 1:
return new Fragment2();
case 2:
return new Fragment3();
default:
return null; // unlikely to happen
}
}
@Override
public int getCount() {
return 3;
}
}
}
即: 不再用 mFragmentList 保存各个 Fragment 的引用了, Fragment 的创建完全交给 FragmentPagerAdapter 去做.
其实在其他的使用 Fragment 的场景中, 也会出现上述问题, 也应该遵循同样的原则, 即文章开头所列的 建议1 和 建议2 .
这样是解决了上面提到的 Activity 销毁恢复的问题, 但如果我们在 Activity 逻辑中, 一定要取到 Fragment 引用, 该怎么办呢. (比如, 点击 ActionBar 上的按钮则改变 Fragment 中的某段文字).
有两种方法可以解决保存 Fragment 引用的问题. |
|
|
|
|
|