1. 程式人生 > >Fragment全解析系列(二):正確的使用姿勢

Fragment全解析系列(二):正確的使用姿勢

Fragment是可以讓你的app縱享絲滑的設計,如果你的app想在現在基礎上效能大幅度提高,並且佔用記憶體降低,同樣的介面Activity佔用記憶體比Fragment要多,響應速度Fragment比Activty在中低端手機上快了很多,甚至能達到好幾倍!如果你的app當前或以後有移植平板等平臺時,可以讓你節省大量時間和精力。


簡陋的目錄
1、一些使用建議
2、add(), show(), hide(), replace()的那點事
3、關於FragmentManager你需要知道的
4、使用FragmentPagerAdapter+ViewPager的注意事項
5、是使用單Activity+多Fragment的架構,還是多模組Activity+多Fragment的架構?


作為一個穩定的app,從後臺且回到前臺,一定會在任何情況都能恢復到離開前的頁面,並且保證資料的完整性。

如果你沒看過本系列的第一篇,為了方便後面文章的介紹,先規定一個“術語”,安卓app有一種特殊情況,就是 app執行在後臺的時候,系統資源緊張的時候導致把app的資源全部回收(殺死app的程序),這時把app再從後臺返回到前臺時,app會重啟。這種情況下文簡稱為:“記憶體重啟”。(螢幕旋轉等配置變化也會造成當前Activity重啟,本質與“記憶體重啟”類似)
<h1 id="1"> 1、一些使用建議 </h1>

1、對Fragment傳遞資料,建議使用setArguments(Bundle args)

,而後在onCreate中使用getArguments()取出,在 “記憶體重啟”前,系統會幫你儲存資料,不會造成資料的丟失。和Activity的Intent恢復機制類似。

2、使用newInstance(引數) 建立Fragment物件,優點是呼叫者只需要關係傳遞的哪些資料,而無需關心傳遞資料的Key是什麼。

3、如果你需要在Fragment中用到宿主Activity物件,建議在你的基類Fragment定義一個Activity的全域性變數,在onAttach中初始化,這不是最好的解決辦法,但這可以有效避免一些意外Crash。詳細原因參考第一篇的“getActivity()空指標”部分。

protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    this.mActivity = activity;
}

<h1 id="2">2、add(), show(), hide(), replace()的那點事</h1>
1、區別
show()hide()最終是讓Fragment的View setVisibility(true還是false),不會呼叫生命週期;

replace()的話會銷燬檢視,即呼叫onDestoryView、onCreateView等一系列生命週期;

add()replace()不要在同一個階級的FragmentManager裡混搭使用。

2、使用場景
如果你有一個很高的概率會再次使用當前的Fragment,建議使用show()hide(),可以提高效能。

在我使用Fragment過程中,大部分情況下都是用show()hide(),而不是replace()

注意:如果你的app有大量圖片,這時更好的方式可能是replace,配合你的圖片框架在Fragment檢視銷燬時,回收其圖片所佔的記憶體。

3、onHiddenChanged的回撥時機
當使用add()+show(),hide()跳轉新的Fragment時,舊的Fragment回撥onHiddenChanged(),不會回撥onStop()等生命週期方法,而新的Fragment在建立時是不會回撥onHiddenChanged(),這點要切記。

4、Fragment重疊問題
使用show()hide()帶來的一個問題就是,如果你不做任何額外處理,在“記憶體重啟”後,Fragment會重疊;(該BUG在support-v4 24.0.0+以上 官方已修復)

有些小夥伴可能就是為了避免Fragment重疊問題,而選擇使用replace(),但是使用show()hide()時,重疊問題很簡單解決的:

<h1 id="3">3、關於FragmentManager你需要知道的</h1>
FragmentManager棧檢視:
(1)每個Fragment以及宿主Activity(繼承自FragmentActivity)都會在建立時,初始化一個FragmentManager物件,處理好Fragment巢狀問題的關鍵,就是理清這些不同階級的棧檢視。

下面給出一個簡要的關係圖

棧關係圖.png

(2)對於宿主Activity,getSupportFragmentManager()獲取的FragmentActivity的FragmentManager物件;

對於Fragment,getFragmentManager()是獲取的是父Fragment(如果沒有,則是FragmentActivity)的FragmentManager物件,而getChildFragmentManager()是獲取自己的FragmentManager物件。

<h1 id="4">4、使用FragmentPagerAdapter+ViewPager的注意事項</h1>

  • 使用FragmentPagerAdapter+ViewPager時,切換回上一個Fragment頁面時(已經初始化完畢),不會回撥任何生命週期方法以及onHiddenChanged(),只有setUserVisibleHint(boolean isVisibleToUser)會被回撥,所以如果你想進行一些懶載入,需要在這裡處理。

  • 在給ViewPager繫結FragmentPagerAdapter時,
    new FragmentPagerAdapter(fragmentManager)的FragmentManager,一定要保證正確,如果ViewPager是Activity內的控制元件,則傳遞getSupportFragmentManager(),如果是Fragment的控制元件中,則應該傳遞getChildFragmentManager()。只要記住ViewPager內的Fragments是當前元件的子Fragment這個原則即可。

  • 你不需要考慮在“記憶體重啟”的情況下,去恢復的Fragments的問題,因為FragmentPagerAdapter已經幫我們處理啦。

<h1 id="5">5、是使用單Activity+多Fragment的架構,還是多模組Activity+多Fragment的架構?</h1>
單Activity+多Fragment:
一個app僅有一個Activity,介面皆是Frament,Activity作為app容器使用。

優點:效能高,速度最快。參考:新版知乎 、google系app

缺點:邏輯比較複雜,尤其當Fragment之間聯動較多或者巢狀較深時,比較複雜。

多模組Activity+多Fragment:
一個模組用一個Activity,比如
1、登入註冊流程:
LoginActivity + 登入Fragment + 註冊Fragment + 填寫資訊Fragment + 忘記密碼Fragment
2、或者常見的資料展示流程:
DataActivity + 資料列表Fragment + 資料詳情Fragment + ...

優點:速度快,相比較單Activity+多Fragment,更易維護。

我的觀點:
權衡利弊,我認為多模組Activity+多Fragment是最合適的架構,開發起來不是很複雜,app的效能又很高效。

當然。Fragment只是官方提供的靈活元件,請優先遵從你的專案設計!真的特別複雜的介面,或者單個Activity就可以完成一個流程的介面,使用Activity可能是更好的方案。

最後

如果你讀完了第一篇和這篇文章,那麼我相信你使用多模組Activity+多Fragment的架構所遇到的坑,大部分都應該能找到解決辦法。

但是如果流程較為複雜,比如Fragment A需要啟動一個新的Fragment B並且關閉當前A,或者A啟動B,B在獲取資料後,想在返回到A時把資料交給A(類似Activity的startActivityForResult),又或者你保證在Fragment轉場動畫的情況下,使用pop(tag\id)從棧內退出多個Fragment,或者你甚至想Fragment有一個類似Activity的SingleTask啟動模式,那麼你可以參考下一篇,我的解決方案庫,Fragmentation。它甚至提供了一個讓你在開發時,可以隨時檢視所有階級的棧檢視的UI介面。



作者:YoKey
連結:https://www.jianshu.com/p/fd71d65f0ec6
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。