Android:Fragment懶加載的實現以及本身的封裝思路

1.什麼是fragment懶加載以及爲何要使用fragment懶加載。

先看下Demo實現的效果吧。你們對這種效果必定不陌生,知乎,掘金等app都用到了這種效果。 git

這裏探索的 懶加載是當 viewpager 結合 tablayout時,多個 fragment在進行數據加載的時候,當且僅當 fragment對用戶可見的時候,才進行數據加載,這裏的數據加載能夠是網絡請求,數據庫請求,是須要消耗資源的,若是不使用懶加載,對於那些用戶未打開的頁面,因爲 viewpager的加載機制,即便用戶未打開的 fragment也有可能進行數據加載,形成資源白白的浪費,所以爲了良好的用戶體驗,懶加載是有必要的。

2.fragment懶加載的實現方式以及封裝。

fragment懶加載實現的關鍵在於其的setUserVisibleHint(isVisibleToUser: Boolean)方法,該方法在fragment對用戶由可見變爲不可見以及由不可見變爲可見時都會回調。咱們建立抽象AbstractLazyInitFrag,對其進行封裝。首先咱們引入isVisibleToUser變量,負責保存當前fragment對用戶的可見狀態。同時還有幾個值得注意的地方:github

  1. setUserVisibleHint(isVisibleToUser: Boolean)方法的回調時機並無與fragment的生命週期有確切的關聯,好比說,回調時機有可能在onCreateView方法以後,也可能在onCreateView方法以前。所以,必須引入一個標誌位isPrepareView判斷view是否建立完成,否則,很容易會形成空指針異常。咱們初始化該變量爲false,在onViewCreated中,也就是view建立完成後,將其賦值爲true。代碼中是這樣:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        isPrepareView = true
    }
複製代碼
  1. 數據初始化只應該加載一次,所以,引入第二個標誌位,isInitData,初始爲false,在數據加載完成以後,將其賦值爲true。至此,咱們的懶加載方法考慮了全部條件。也就是當isVisibleToUsertrueisInitDatafalseisPrepareViewtrue時,進行數據加載,而且加載後爲了防止重複調用,將isInitData賦值爲true。代碼以下:
private fun lazyInitData() {
        if (!isInitData && isVisibleToUser && isPrepareView) {
            isInitData = true
            initData()
        }
    }
複製代碼

其中initData()爲抽象方法,由子類實現,在這裏操做數據加載的邏輯。數據庫

  1. 該方法的調用時機。首先是 setUserVisibleHint(isVisibleToUser: Boolean)方法中是必須調用的。代碼以下:
/*當fragment由可見變爲不可見和不可見變爲可見時回調*/
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        this.isVisibleToUser = isVisibleToUser //標誌位保存fragment對用戶的可見狀態
        lazyInitData()
    }
複製代碼

其次,很容易忽略的一點。對於上圖中第一個fragment,若是setUserVisibleHint(isVisibleToUser: Boolean)方法在onCreateView以前調用的話,若是懶加載方法只在setUserVisibleHint(isVisibleToUser: Boolean)中調用,那麼該fragment將只能在被主動切換一次以後才能加載數據,這確定是不可能的,所以,咱們須要在view建立完成以後,也進行一次調用。思來想去,在onActivityCreated方法中是最合適的。咱們在繼承的時候,在onViewCreated方法中進行一些初始化就好了,這樣不會引發衝突。代碼以下:bash

/*fragment生命週期中onViewCreated以後的方法 在這裏調用一次懶加載 避免第一次可見不加載數據*/
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        lazyInitData()
    }
複製代碼

貼上完整的封裝好的抽象基類fragment:網絡

abstract class AbstractLazyInitFrag : Fragment() {

    private var isInitData = false /*標誌位 判斷數據是否初始化*/
    private var isVisibleToUser = false /*標誌位 判斷fragment是否可見*/
    private var isPrepareView = false /*標誌位 判斷view已經加載完成 避免空指針操做*/

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
       return inflater.inflate(getLayoutId(), container, false)
    }

    abstract fun getLayoutId(): Int

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        isPrepareView = true
    }

    /*加載數據的方法,由子類實現*/
    abstract fun initData()

    /*懶加載方法*/
    private fun lazyInitData() {
        if (!isInitData && isVisibleToUser && isPrepareView) {
            isInitData = true
            initData()
        }
    }

    /*當fragment由可見變爲不可見和不可見變爲可見時回調*/
    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        this.isVisibleToUser = isVisibleToUser
        lazyInitData()
    }

    /*fragment生命週期中onViewCreated以後的方法 在這裏調用一次懶加載 避免第一次可見不加載數據*/
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        lazyInitData()
    }
}
複製代碼

順便貼下Demo中測試的繼承的子類:app

class FragLazyInitTest : AbstractLazyInitFrag() {

    private val dataList = ArrayList<String>()
    private val adapter = ListAdapter(dataList)

    /*初始化通常在這裏實現*/
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        recyclerView.layoutManager = LinearLayoutManager(context)
        recyclerView.adapter = adapter
    }

    override fun getLayoutId(): Int {
        return R.layout.frag_lazy_init_test
    }

    override fun initData() {
        /*模擬的加載數據過程,實際場景通常是網絡請求或者數據庫等耗時操做*/
        swipeRefreshLayoutFLIT.isRefreshing = true
        swipeRefreshLayoutFLIT.postDelayed({
            swipeRefreshLayoutFLIT.isRefreshing = false
            dataList.add("data1")
            dataList.add("data2")
            dataList.add("data3")
            dataList.add("data4")
            dataList.add("data5")
            dataList.add("data6")
            dataList.add("data7")
            dataList.add("data8")
            adapter.notifyDataSetChanged()
        }, 2000)
    }
}
複製代碼

3.Demo地址

github.com/Lesincs/Laz…ide

4.最後

這是第一次在掘金上寫文章,也幾乎是第一次寫博客吧。感受描述的很凌亂,你們若是有疑問能夠去看看Demo,而後多多交流.post

相關文章
相關標籤/搜索