使用Fragment可以讓APP更加的流暢藕赞,并且降低內(nèi)存占用,同樣的界面Activity占用內(nèi)存比Fragment要多桑阶,相應(yīng)速度Fragment比Activity在中低端手機(jī)上快了很多悟泵。但是在Fragment的使用過程中總會出現(xiàn)一些bug,比如Fragment嵌套或者是單Activity+多Fragment架構(gòu)時鱼冀。
1.getActivity()空指針
可能你會遇到過在Fragment中使用getActivity時返回null报破,大多數(shù)情況下的原因是:你在調(diào)用了getActivity時,當(dāng)前的Fragment已經(jīng)onDetach()了宿主的Activity千绪。
解決方法:在Fragment基類里設(shè)置一個Activity mActivity的全局變量充易,在onAttach里面復(fù)制,使用mActivity代替getActivity荸型,保證Fragment即使在onDetach后盹靴,仍持有Activity引用。
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
/**
- 如果你用了support 23的庫瑞妇,上面的方法會提示過時稿静,有強(qiáng)迫癥的小伙伴,可以用下面的方法代替
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity)context;
}
2.Fragment重疊異常
如果你add()了幾個Fragment辕狰,使用show()改备、hide()方法控制,比如微信蔓倍、qq底部tab等場景悬钳,如果你什么都不做的話,在“內(nèi)存重啟”后回到前臺偶翅,app的這幾個Fragment界面會重疊默勾。
原因是FragmentManager幫我們管理Fragment,每當(dāng)我們離開該Activity聚谁,F(xiàn)ragmentManager都會保存它的Fragments灾测,當(dāng)發(fā)生“內(nèi)存重啟”,他會從棧低向棧頂回復(fù)Fragment垦巴,并且全部都是以show的方式,所以我們看到了界面重疊铭段。
解決方法:
(1)即在add()或者replace()時綁定一個tag骤宣,一般我們使用相應(yīng)Fragment的類名作為tag,然后在發(fā)生“內(nèi)存重啟”時序愚,通過findFragmentByTag找到相應(yīng)的Fragment憔披,并hide()需要隱藏的Fragment。
標(biāo)準(zhǔn)寫法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “內(nèi)存重啟”時調(diào)用
targetFragment = getSupportFragmentManager().findFragmentByTag(targetFragment.getClass().getName);
hideFragment = getSupportFragmentManager().findFragmentByTag(hideFragment.getClass().getName);
// 解決重疊問題
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常時
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, targetFragment, targetFragment.getClass().getName())
.add(R.id,container,hideFragment,hideFragment.getClass().getName())
.hide(hideFragment)
.commit();
}
}
如果你想恢復(fù)到用戶離開時的那個Fragment界面爸吮,你還需要在onSaveInstanceState里保存離開時的那個Fragment的tag或者下標(biāo)芬膝,在onCreate“內(nèi)存重啟”代碼中取出tag或者下標(biāo),進(jìn)行恢復(fù)形娇。
(2)使用getSupportFragmentManager.getFragments()恢復(fù)锰霜。
通過getFragments()可以獲取當(dāng)前FragmentManager管理的棧內(nèi)所有Fragment
標(biāo)準(zhǔn)寫法如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “內(nèi)存重啟”時調(diào)用
List fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if(fragment instanceof TartgetFragment){
targetFragment = (TargetFragment)fragment;
}else if(fragment instanceof HideFragment){
hideFragment = (HideFragment)fragment;
}
}
// 解決重疊問題
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常時
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
// 這里add時,tag可傳可不傳
getFragmentManager().beginTransaction()
.add(R.id.container)
.add(R.id,container,hideFragment)
.hide(hideFragment)
.commit();
}
}
從代碼看起來這種方法比較復(fù)雜桐早,但是這種方法在一些場景下比第一種方法更加簡單有效癣缅。順便提一下厨剪,有些小伙伴會用一種并不合適的方法恢復(fù)Fragment,雖然效果也能達(dá)到友存,但并不恰當(dāng)祷膳。
// 保存
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, KEY, targetFragment);
}
// 恢復(fù)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
if (savedInstanceState != null) {
Fragment targetFragment = getSupportFragmentManager().getFragment(savedInstanceState, KEY);
}
}
如果僅僅為了找回棧內(nèi)的Fragment,使用putFragment保存Fragment是完全沒有必要的屡立。因為FragmentManager在任何情況都會把你存儲Fragment直晨,你要做的僅僅是在“內(nèi)存重啟”后找到這些Fragment。