記一次Fragment引起的血案

Fragment引起的血案

前幾天遇到一個很奇怪的問題,主頁Fragment的onResume中的接口,存在偶現的頻繁刷接口的現象。
看上去代碼一切正常,我們也重現不出來,但是線上就是存在刷接口的現象,被我們運維頻頻吐槽。
在百思不得其解之際,終於找到突破口 : 自己對於Fragment的使用一直存在誤區。

Fragment的使用誤區

Fragment在Activity旋轉屏幕或重建後,並不會銷燬,而是存儲在saveInstanceState中。
先附上我們平時對於Fragment的用法,( 這是錯誤的 )
如果Activity發生了重建,這會導致內存中,存在兩份Fragment,
甚至於,該Activity多次重建後,就會有多個Fragment同時存在,
而這些Fragment都會重新走生命週期,導致生命週期中的接口被多次頻繁調用。

getSupportFragmentManager().beginTransaction()
                .add(R.id.layout_container, SecondFragment.newInstance(), "SecondFragment")
                .commit();

方案一

Fragment的show和add方法應該結合使用,如果能夠找到對應的tag的fragment,應該要show Fragment,而不是新創建一個fragment來add

正確用法:

Fragment fragment = getSupportFragmentManager().findFragmentByTag("SecondFragment");
if (fragment != null) {
    getSupportFragmentManager().beginTransaction()
            .show(fragment)
            .commit();
}else{
    getSupportFragmentManager().beginTransaction()
            .add(R.id.layout_container, SecondFragment.newInstance(), "SecondFragment")
            .commit();
}

方案二

在所有fragment的使用中,都判斷show和add,相對比較繁瑣,如果對於fragment重用沒有要求,那麼我們完全可以禁止fragment的緩存,從而一勞永逸,解決這個問題。

在Activity的onSaveInstanceState,加入如下代碼

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    // 只要發生onSaveInstanceState就remove all Fragment
    if (outState != null) {
        Log.i(TAG, "outState.remove");
        outState.remove("android:support:fragments");
    }
}

方案三

如果我們對大廠的App們進行測試,會發現一個驚人的祕密,大廠的App完全禁止了App的SaveInstanceState,即不用再考慮App銷燬重建時,數據的存儲恢復情況,這不僅僅可以避免fragment的問題,還可以避免因遺漏對某些數據進行存儲/恢復處理,導致的異常情況的發生。
當然這也有代價,那就是系統回收了App後,無法保存現場頁面狀態和數據。

@Override
public void onSaveInstanceState(Bundle outState) {
	//註釋掉這行
    //super.onSaveInstanceState(outState);
}

爲什麼我們的Activity會發生重建

我們的app的旋轉方向限定爲豎屏,理論上不會發生Activity重建的情況。
那是什麼場景導致Activity重建呢 ? 搜尋資料後發現

在 Android 的 API 21 ( Android 5.0 ) 以下,Crash 會直接退出應用,但是在 API 21 ( Android 5.0 ) 以上,系統會遵循以下原則進行重啓:

  • 包含 Service,如果應用 Crash 的時候,運行着Service,那麼系統會重新啓動 Service。
  • 不包含 Service,只有一個 Activity,那麼系統不會重新啓動該 Activity。
  • 不包含 Service,但當前堆棧中存在兩個 Activity:Act1 -> Act2,如果 Act2 發生了 Crash ,那麼系統會重啓 Act1。
  • 不包含 Service,但是當前堆棧中存在三個 Activity:Act1 -> Act2 -> Act3,如果 Act3 崩潰,那麼系統會重啓 Act2,並且 Act1 依然存在,即可以從重啓的 Act2 回到 Act1。

國內有些廠商可能會去除這個功能

所以,我們有時候出現崩潰,會發現App並沒有閃退,而是會恢復到上一個頁面 (上一個頁面進行了重建)。

小結

至此,我們知道了Fragment的使用中,add/show的誤區,並給出瞭解決方案。
並說明了Activity重建的情況,擴展了自己的認知邊界。
在日後的工作中,我們應該要避免Fragment錯誤使用的情況。

最後,附上用來重現該問題的Demo

相關資料

Android 解決應用崩潰後重啓的問題
解決Fragment中調用getActivity()爲null的多種方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章