Android 管理多个fragment(处理Activity被回收的情况)

做了好久的项目,由于进度赶,也没怎么闲心来写博客。不过项目赶是赶,也不是一口就能吞掉的,要慢慢来。既然慢慢来,就抽出点时间来写点什么。在项目里遇到好几个棘手的问题。有几个是被我解决掉。其中一个就是多个fragment的管理问题。只能说我是android的新手,也没怎么系统学习android,所以有些东西,只能在项目里摸索,要用到哪个就学哪个,还好我还大概知道哪些该在哪用,哪些该是被学。


**情景一:**一个Activity中使用多个fragment,以tab的形式来点击切换fragment。一共用到了4个fragment,用一个全局的fragment数组保存了所有fragment的引用。在onCreate方法中完成实例化(默认显示第一个fragment)。

1
mContentFragments = new BaseFragment[4];

这样会造成一个问题,就是比如:当前你切换到了第2个fragment,应用切换到了后天,这时候这个Activity在后台被回收了,然后你不知道,你切回应用,Activity又重新调用了activity方法。然后你就憋屈了,因为应用又回到第一个fragment。
解决办法(如有高手有其他解决办法请告知在下!):

1

st=>start: 应用进入后台
e=>end: 回到应用正常
op1=>operation: 调用onSaveInstanceState保持状态
cond=>condition: aty在后台被杀死?
op2=>operation: 调用onRestoreInstanceState恢复状态

st->op1->cond
cond(yes)->op2->e
cond(no)->e

由于我们一旦按了Home键,应用会转后台,aty会自动调用onSaveInstanceState保持状态,所以我们在onSaveInstanceState的方法里保存状态。aty在后台被杀死后,onSaveInstanceState中有保存状态,回到aty时会自动调用onRestoreInstanceState方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@Override
protected void onSaveInstanceState(Bundle outState)
{

// mCurrentPosition表示当前切换的fragment的数组下标
outState.putInt(POSITIONKEY, mCurrentPosition);
//super.onSaveInstanceState(outState); 总是执行这句代码来调用父类去保存视图层的状态,会导致fragmen重影,不能正常恢复状态
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{

// 获取保存的数组下标
mCurrentPosition=savedInstanceState.getInt(POSITIONKEY);
// 回复视图状态,恢复为fragmen的切换状态
changeTabView(savedInstanceState.getInt(POSITIONKEY));
super.onRestoreInstanceState(savedInstanceState);
}

**情景二:** aty被回收了,fragment没被回收(还在内存中,只是我们没有它们的引用)!
先晾一下切换fragment的代码先:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public void changeFragment(int resView, Fragment targetFragment)
{

if (targetFragment.equals(currentFragment))
{
return;
}
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
if (!targetFragment.isAdded())
{// 如果这个fragment没被添加过
transaction.add(resView, targetFragment, targetFragment.getClass()
.getName());
}
if (targetFragment.isHidden())
{// 如果这个fragment是被隐藏的
transaction.show(targetFragment);
}
if (currentFragment != null && currentFragment.isVisible())
{// 隐藏当前显示的fragment
transaction.hide(currentFragment);
}
currentFragment = targetFragment;
// 提交了才算完成
transaction.commit();
}

在这里的代码,我使用的是FragmentTransaction里的add、show、hide方法,这样在fragment不多的情况下,可以保存fragment的状态,并提高了切换fragment的速度。但是这样的缺点在该情景下就有问题了。
aty被回收,fragment还在内存中,在FragmentManager里,重新onCreate,重新实例化了新的一批fragment,然后一调用changeFragment方法。比如,FragmentManager里有一个FragmentA,我们又重新实例化一个FragmentA。由于两个FragmentA的对象在堆中不是同一个,故FragmentTransaction会add这个新的FragmentA到FragmentManager中(会造成fragment重影),造成内存泄露。
所以,我们应该这么做(如有高手有其他解决办法请告知在下!):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@Override
public void onCreate(Bundle savedInstanceState)
{

if (savedInstanceState != null)
{
// 防止fragment没被回收
FragmentManager fm = getFragmentManager();

mContentFragments = new BaseFragment[4];
mContentFragments[0] = (BaseFragment) fm.findFragmentByTag(MainChatFragment2.class.getName());
mContentFragments[1] = (BaseFragment) fm.findFragmentByTag(MainCircleFragment.class.getName());
mContentFragments[2] = (BaseFragment) fm.findFragmentByTag(MainWallFragment.class.getName());
mContentFragments[3] = (BaseFragment) fm.findFragmentByTag(MainFindFragment.class.getName());

}
super.onCreate(savedInstanceState);
}