Fragment實戰總結
更少的廢話,更多的知識
普羅古拉姆
前言:這幾年想要專心做技術了,起步比較渣,但相信堅持會帶來改變,之前的公眾號現在拿來寫技術博客了,當然,也會寫一些其他有趣的東西。
Fragment
生命周期
Fragment是Android開發中必不可少的組件,這裡就不介紹Fragment的定義和基本用法了,感興趣可以自己去找找,在使用任何Android 視圖組件的時候,一定要先搞清楚生命周期,這樣你才能清楚的了解到現在的視圖的狀態是什麼樣子的。如圖,Fragment的生命周期可以分為11段,對比前面的activity7段來說,多了不少。
①onAttach生命周期走到這個方法的時候,fragment已經和activity綁定好了。方法經常用來對context進行賦值,傳進來的context是fragment依附的activity。比如下面的代碼。
@OverridepublicvoidonAttach(Context context){super.onAttach(context); activity = (Activity) context; Log.i("zyy_test", "Fragment" + str + " onAttach"); }
②onCreate方法中經常用來接收fragment newInstance中傳入的數據。比如下面的代碼
publicstatic BaseFragment newInstance(String str){ Bundle args = new Bundle(); args.putString("str", str); BaseFragment fragment = new BaseFragment(); fragment.setArguments(args);return fragment; }@OverridepublicvoidonCreate(@Nullable Bundle savedInstanceState){super.onCreate(savedInstanceState); Bundle bundle = getArguments(); str = bundle.getString("str"); Log.i("zyy_test", "Fragment" + str + " onCreate"); }
③onCreateView方法常用來初始化view,相當於activity oncreate方法中的setContentView方法。比如下面的代碼
@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState){ View view = inflater.inflate(R.layout.fragment_layout, null); TextView txt = view.findViewById(R.id.txt); txt.setText(str); Log.i("zyy_test", "Fragment" + str + " onCreateView");return view; }
④onActivityCreate生命周期走到這個方法的時候,activity已經準備好了,比如在某些場景,fragment需要使用到activity中的一些方法或者變量,如果activity沒有完全創建好就使用,獲得的數據就是默認值。
總結一下:
1、靜態的view不需要使用這個方法
2、保存view狀態對的時候需要這個方法。
3、訪問父activity view層的時候需要這個方法。
@OverridepublicvoidonActivityCreated(@Nullable Bundle savedInstanceState){super.onActivityCreated(savedInstanceState); Log.i("zyy_test", "Fragment" + str + " onActivityCreated"); }
其他的生命周期階段平平無奇,這裡就不介紹了。大家可以自己在activity中寫一個fragment,觀察一下fragment和activity的生命周期的關係。這個也平平無奇,沒啥可說的。
接下來重點介紹一下fragment在viewpager中的生命周期變化。fragment用在viewpager中的場景數不勝數,今日頭條,掌上生活發現頁,各種內容流app都有。但fragment+viewpager之後,fragment的生命周期的觸發時機有點不一樣。
viewpager有個預加載的特點,這裡不展開說,只要知道有這麼個事就行。預加載的值最小是1。也就是說viewpager會加載當前的fragment和當前fragment左右各1個fragment。在這裡,fragment還有個生命周期階段叫setUserVisibleHint,代碼如下。
@OverridepublicvoidsetUserVisibleHint(boolean isVisibleToUser){super.setUserVisibleHint(isVisibleToUser); Log.i("zyy_test", "Fragment" + str + " setUserVisibleHint " + isVisibleToUser); }
在viewpager中的fragment生命周期的第一階段不是onAttach,而是setUserVisibleHint,這個方法用來判斷,當前fragment是否可見,這個方法對於viewpager中Fragment的數據懶加載起到至關重要的作用。
也就是說在viewpager中,一共有三個fragment的生命周期會走到onResume這個方法,三個fragment都做好了,顯示的準備。但是只有一個fragment顯示了,就是那個被用戶選中的fragment。現在有ABC三個fragment,B是顯示的,如果用戶此時將Bfragment劃到Cfragment,那麼此時viewpager做的事情就是預創建Dfragment,並且將Afragment結束掉,但是不會onDestroy,更會不onDetach。只是走到onDestroyView(把view銷毀了),而剛才劃出屏幕的BFragment,通過控制臺的log可以發現,什麼都沒有列印。這意味著,Bfragment的生命周期一個都沒有走,此時Bfragment依然是一個活蹦亂跳的view,只是不可見罷了。
當開發者了解到viewpager中fragment的生命周期變化時機後。代碼開發就會變得得心應手了。
很遺憾我的電腦無法打開github網頁,目前代碼無法上傳,無法供大家一起探討。大家可以自行寫個小demo驗證上述說明。
Fragment
懶加載
這個懶加載主要是針對viewpager中使用fragment來說的,因為我們有時並不希望,一次加載多頁fragment數據,當翻到某個頁面的時候再加載數據就行,這樣做的一個好處就是能提高軟體性能,並且幫助用戶節省了流量。
fragment懶加載最重要的方法就是剛才介紹過的setUserVisibleHint方法。我們在fragment中設置一個變量比如叫isVisisble,用來判斷fragment是否可見,我們只需要在setUserVisibleHint方法中將isVisibleToUser這個變量賦值給isVisisble。另外還需要設置一個變量,用來判斷fragment依附的activity是否準備好了。比如叫isPerpared當系統回調onActivityCreate時,將這個值置為true即可。通過這兩個變量,可以確保懶加載的順利進行。
Fragment
實戰問題解決
這裡分享一個我在實際工作中遇到的一個關於fragment+viewpager+tablayout的問題。
問題背景:我做了一個功能,有多個切換tab,需要是每次切換的時候都刷新,刷新的時候會從服務端獲取數據,我會傳遞一個時間戳給服務端,第一次進頁面傳遞null,下發所有的數據,這是所謂的全量查詢。後面再請求,傳遞上次下發的數據的最後一條時間戳,服務端就將這條數據之後的數據下發,這就是所謂的增量查詢數據。訪問服務端都是異步進行的,數據從服務端拿到後,我會回調顯示。
問題描述:當我進入頁面後,在兩個未展示過的tab之間,來回快速點擊,然後發現本來應該只有一條數據,但是經過操作後,發現會出現2條甚至更多重複的數據。
問題原因:當我快速交替點擊的時候,兩個頁面都在不停地向服務端發送請求,最為致命的是,如果服務端響應比較慢,而我手速比較快,那麼當我第一點擊Afragment的時候發送了一個時間戳為null的請求,然後交替點擊再次點擊Afragment的時候服務端請求還沒下來,我又發送了了一次時間戳為null的請求,這樣服務端就返回了兩次全量數據。
解決方案:我設置了一個原子類型(AtomicBoolean)的布爾變量isSync。大家感興趣可以自己查一下原子類型。當我調用服務端方法的時候,我會先判斷isSync是否為true,為true表示正在調用,則不會開啟本次調用。如果為false,則首先將isSync置為true,然後再調用服務端。當數據返回後,再將isSync置為false,表示本次調用結束。這樣,同一個fragment就不會多次調用服務端,而導致數據異常的問題了。
注意:這個變量是加在fragment中的,不是網絡訪問代碼中,所以這個變量只對同一個fragment起作用,不同fragment之間是沒有影響的。這樣就保證了不同fragment之間的並發訪問服務端數據。
很開心自己能開始寫技術博客了,希望能堅持下去,我的目標是成為一名Android高級工程師。我不關心有沒有人看我的博客,但是歡迎大家指出我任何方面的不足。
一起來變強吧!
圖片來源:花瓣@斑斕Oo