Android 談談封裝那些事 –BaseActivity 和 BaseFragment(二)

hankkin發表於2019-03-01

1.前言

昨天談了BaseActivity的封裝,
Android談談封裝那些事–BaseActivity和BaseFragment(一)
有很多小夥伴提了很多建議,比如:

  • 通用標題欄可以自定義View而不放在Base裡面,程式碼更統一
  • BaseEventActivity裡面應該留出開關保證不需要Bus的Activity使用
  • BaseStatusActivity裡面就一個方法沒必要新建一個
  • 還有一些小的細節

在這裡感謝大家的建議了啊。我修改了一部分,後面會慢慢優化,最後在HLibrary裡面貼出最優程式碼。還有那個BaseStatusActivity(沉浸欄)的也會繼續優化的。接下來談談BaseFragment的封裝,其實主要還是關於Fragment的懶載入問題。

2.Fragment相關知識點

  • 生命週期
  • Fragment的使用:靜態、動態
  • Fragment應注意到的問題
  • ……

在這裡就不詳細介紹了,想了解的可以看我的這篇文章:

你真的會用Fragment了麼?-Fragment解析

1.Fragment使用場景

我們可以看到市場的APP一般都是用底部Tab+fragment切換為整體架子的,或者通過viewpager作為容器巢狀fragment,再複雜點的就是fragment巢狀fragment,某書、某條等等都是這樣;

2.遇到的問題

這樣的話我們遇到的問題就是當fragment很多組合使用的時候,每個fragment裡面都會載入資料或者執行動畫等比較複雜的業務邏輯時,導致我們的APP進入時螢幕卡頓,效能很差,一點都不流暢。即便是我們在使用viewpager作為容器的時候設定預載入setOffscreenPageLimit()這個方法,其實你會發現根本不會起作用。
至於viewpager這個預載入方法為什麼不行?我們可以看一下viewpager原始碼:

Android 談談封裝那些事 –BaseActivity 和 BaseFragment(二)

我們可以看到DEFAULT_OFFSCREEN_PAGES 這裡就定義了預設值是1,如果你呼叫該方法傳進來的值小於1是無效的,會被強行的拽回1。而且DEFAULT_OFFSCREEN_PAGES 這個值是private的,子類繼承ViewPager也是不可見的。
網上有的說可以將viewpager的原始碼複製下來粘到自己的類裡面將這個預設值改為0,這個目前還沒試過,即使可以感覺也有些彆扭,更何況我們使用場景也不一定需要viewpager呢

3.Fragment懶載入

這是靠Fragment裡有一個setUserVisibleHint(boolean isVisibleToUser)的方法,我們可以在這個方法裡做判斷,當其True可見時(即切換到某一個具體Fragment)時才去載入資料,這樣可以省流量。
具體怎麼去操作呢?

  • 預載入初始化資料和元件等輕量操作
  • 切換tab到第一次可見時執行網路請求
  • 四種狀態:第一次可見狀態、可見狀態、第一次不可見狀態、不可見狀態
  • 銷燬時處理解註冊、銷燬廣播等問題

3.BaseFragment封裝

1.初始化xml檔案

@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (getContentViewLayoutID() != 0) {
            return inflater.inflate(getContentViewLayoutID(), null);
        } else {
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    }

        protected abstract int getContentViewLayoutID();複製程式碼

2.註解繫結以及初始化元件

@Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ButterKnife.bind(this,view);
        initViewsAndEvents(view);
    }

    protected abstract void initViewsAndEvents(View view);複製程式碼

3.四種“見”的狀態拆分

@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initPrepare();
    }

    private synchronized void initPrepare() {
        if (isPrepared) {
            onFirstUserVisible();
        } else {
            isPrepared = true;
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            if (isFirstVisible) {
                isFirstVisible = false;
                initPrepare();
            } else {
                onUserVisible();
            }
        } else {
            if (isFirstInvisible) {
                isFirstInvisible = false;
                onFirstUserInvisible();
            } else {
                onUserInvisible();
            }
        }
    }

    protected abstract void onFirstUserVisible();
    protected abstract void onUserVisible();
    private void onFirstUserInvisible() { }
    protected abstract void onUserInvisible();複製程式碼

4.最後解註冊銷燬等工作

@Override
    public void onDestroy() {
        DetoryViewAndThing();
        super.onDestroy();
    }

    protected abstract void DetoryViewAndThing();複製程式碼

5.跳轉介面等方法

/**
     * 開啟一個Activity 預設 不關閉當前activity
     */
    public void gotoActivity(Class<?> clz) {
        gotoActivity(clz, false, null);
    }

    public void gotoActivity(Class<?> clz, boolean isCloseCurrentActivity) {
        gotoActivity(clz, isCloseCurrentActivity, null);
    }

    public void gotoActivity(Class<?> clz, boolean isCloseCurrentActivity, Bundle ex) {
        Intent intent = new Intent(mActivity, clz);
        if (ex != null) intent.putExtras(ex);
        startActivity(intent);
        if (isCloseCurrentActivity) {
            mActivity.finish();
        }
    }複製程式碼

最後提一個小問題吧:就是說如果你沒有用Fragment懶載入的話而是用的viewpager的預載入,如果你沒有自己定義預設預載入個數的話,那麼預設肯定是會提前載入的,加入你當前fragment相鄰的fragment裡面有動畫或者視訊播放的話,切換到當前fragment時候下個fragment裡面的動畫或者視訊就已經開始執行了,注意一下。
到此我的base就告一段落了,很多不合理的地方,我接下來私下也會去聽取小夥伴們的意見進行修改,然後最後在HLibrary裡面貼出來的,非常感謝大家。

相關文章