讓普通 Java 類自動感知 Activity Lifecycle

《億級 Android 架構》 地址:https://xiaozhuanlan.com/AndroidArch

背景

在 Android 開發中,我們都很熟悉 Activity 的 Lifecycle,並且會在特定的 Lifecycle 下執行特定的操作。當然,我們清楚 Lifecycle 本身是帶有 Android 特質的,那嘗試設想下,如果普通的 Java Class 也能自動感知 Lifecycle 呢?咋一聽這個想法似乎背後意義不大,但在實際探索中,我們發現這個特性能爲我們達成一些之前未考慮到或者不易實現的優化。

本文分享下我們基於這個思想所開發的框架:AutoLifecycle 及其帶來的一些有意思的實踐。

  • 優化一:當Activity進入onDestroy時,自動取消網絡請求返回
  • 優化二:自動將網絡請求時機提前到View渲染之前,提高頁面打開速度
  • 優化三:MVP改進,讓Presenter和View自動bind/unBind

注:下文提到的Lifecycle-Aware就是這裏指代的讓普通 Java Class 自動獲取 Lifecycle

實踐及優化

優化一:當Activity進入onDestroy時,自動取消網絡請求返回

在網絡請求時,相信大家都有一個經驗:在每個網絡結果回來時,我們做的第一件事不是顯示數據,而是寫個if-else判斷Activity還在不在。

mTopApiObservable
  ...
  .subscribe(new Subscriber<Object>() {
      @Override
      public void onNext(Object data) {
        if(activity == null) {
            return; // 判斷Activity是否還在,不在就不去顯示數據
        }
        
        display(data); // 顯示數據
      }
      ...
  });

由於網絡請求都是異步的,所以不得不做這樣的判斷,來防止不可預測的空指針問題或內存泄漏問題。

既然你總是擔心Activity還在不在,那麼如果我們通過Lifecycle-Aware讓每個網絡請求能自動感知Activity的onDestroy事件
並在onDestroy時,自動把網絡請求結果取消掉不再返回,那就能夠消除這個擔憂了。

mTopApiObservable
  ...
  .compose(bindUntilEvent(ActivityLifecycle.DESTROY)) // 綁定Activity的onDestroy事件
  .subscribe(new Subscriber<Object>() {
      @Override
      public void onNext(Object data) {
        display(data); // 直接去顯示數據
      }
      ...
  });

其中最關鍵的就是compose(bindUntilEvent(ActivityLifecycle.DESTROY))這句,它能達到的效果是:一旦Activity發生onDestroy時,Observer的數據就會停止向Subscriber裏流動。從而保證onNext無需擔心ActivityDestroy這種情況。

在上面網絡請求的實踐裏,你還可以根據自己的情況把Destroy換成Stop/Pause等,而且可以看出,這種自動取消機制可適用於任何Observable,不僅僅是網絡請求。

優化二:自動將網絡請求提前到View Inflate之前,加速頁面渲染

先說下這項優化的原理。
通常,我們會在ActivityonCreate裏依次執行下面的代碼:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.XXX);   // Inflate View
    findViewByIds();                // 初始化View
    presenter.loadDataFromServer(); // 發起網絡請求
}

即在Inflate View初始化View之後,才發起網絡請求去加載數據。

而實際上,網絡請求是不佔用主線程的,如果能在Inflate View之前就在其他線程發起網絡請求,可以把整個頁面顯示耗時縮短100ms-200ms

281665-dbf719e2d9a88946.png
LoadBeforeInflate優化效果 (1).png

現在有了AutoLifecycle框架,我們就可以很輕鬆實現:讓Presenter自動監聽Inflate View這個生命週期,在那時發起網絡請求即可。

public class NewPresenter {

    public NewPresenter(IView iView) {
        ...
        // 向AutoLifecycle註冊
        AutoLifecycle.getInstance().init(this); 
    }

    // 當Activity Inflate View前自動回調
    @AutoLifecycleEvent(activity = ActivityLifecycle.PRE_INFLATE)
    private void onHostPreInflate() {
         loadDataFromServer(); // 發起網絡請求
    }
    ...
}

此時,我們的Activity也不用手動調用presenter.loadDataFromServer();了,因爲Presenter內會在感知到Inflate View事件時自動發起網絡請求。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.XXX);
    findViewByIds();
    // 無需手動啓動網絡請求
}

經過測試,在保證單個網絡請求耗時相同的情況下,頁面從onCreate顯示數據的渲染耗時可以從550ms縮短到367ms,也就是30%-40%的優化,效果是非常不錯的,而且代碼也更加簡潔清晰。

優化前

優化後
[圖片上傳失敗...(image-889271-1510146010290)]

通過簡單的註冊AutoLifecyclePresenter能夠自動感知到所有Lifecycle,甚至包括自定義的特殊Lifecycle,如下圖:
[圖片上傳失敗...(image-8c0f0b-1510146010290)]

281665-ba24e7873f08472a.png
B2.png

優化三:MVP改進,讓Presenter和View自動bind/unBind

第一項優化比較直接,可以先讓大家形成一個直觀印象。
我們項目是採用MVP項目,對於Presenter的使用存在一段固定代碼,即在onCreate時調用bindView(),在onDestroy時調用unBindView()。如下圖:

public class OldActivity extends BaseActivity {

    BasePresenter mPresenter = new BasePresenter();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter.bindView(this); // onCreate時手動bind Presenter 和 IView
    }

    @Override
    protected void onDestroy() {
        mPresenter.unbindView(); // onDestroy時手動unBindView
        super.onDestroy();
    }
}

那麼,既然我們現在能讓一個普通類自動感知Lifecycle,那其實也就能讓Presenter在感知到onCreate自動bindView,在感知到onDestroy自動unBindView
改進後的代碼如下:

public class NewActivity extends BaseActivity {

    NewPresenter mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = new NewPresenter(this); // 只需要創建即可
    }
}

public class NewPresenter {

    private IView mIView;

    public NewPresenter(IView iView) {
        this.mIView = iView;
        // 向AutoLifecycle註冊即可獲得Lifecycle回調
        AutoLifecycle.getInstance().init(this); 
    }

    // 當Activity進入onCreate後自動調用
    @AutoLifecycleEvent(activity = ActivityLifecycle.CREATE)
    private void onHostCreate() {
        bindView(mIView); 
    }

    // 當Activity進入onDestroy後自動調用
    @AutoLifecycleEvent(activity = ActivityLifecycle.DESTROY)
    private void onHostDestroy() {
        unBindView();
    }
}

其實,在大家的平常開發中,還會存在許多類似Presenter的類:需要在某個特定的Lifecycle下執行一些動作。這時就可以基於Lifecycle-Aware來讓這個普通類自動去執行,而不是去每個Activity/Fragment裏寫一遍,提高類的內聚性。

AutoLifecycle的核心原理

(TL;DR)
下面介紹下AutoLifecycle的關鍵實現部分,感興趣的讀者可以參考。

1. 讓Activity對外發送Lifecycle事件

使用過RxJava的同學知道里面有一個PublishSubject,基於觀察者模式,主動發送並接受消息。這裏我們用PublishSubject來發送Lifecycle事件。見如下:

281665-2deba2e78165c91a.png
D1.png

這裏的Lifecycle事件可以自己定義,比如前面提到的PRE_INFLATE事件,是在setContentView之前發送,類似:

281665-f7c747443273ebe4.png
D2.png

2. 感知某個Lifecycle的發生並自動執行回調

上面提了,PublishSubject不僅能發送消息,還能接受自己的消息。基於這個特點,我們便可以監聽每一個LifecycleEvent。如下圖:

281665-0f2d95662cd053f4.png
D3.png

這裏的參數Observable是我們希望被回調的函數,IContextLifecycle是指定的Lifecycle。即當指定的Lifecycle Event發生時,會自動subscribe提供的Observable。

基於這個功能,便可以實現上面場景一和場景二里的@AutoLifecycleEvent註解了,即把@AutoLifecycleEvent標註的函數包裝成一個Observable,通過這個executeOn來註冊函數的執行生命週期即可。

3. 監聽Lifecycle並取消網絡請求結果

在場景三裏,我們爲網絡請求的Observable提供了一個Transformer,它能在監聽到某個Lifecycle發生時,停止數據流的向下流動。該Transformer的核心實現是:

281665-3de275bde3f6a0a1.png
D4.png

可以看出,當指定的Lifecycle一旦發生,我們網絡請求Observable就會停止向下傳遞數據。

4. 支持自定義Lifecycle,支持Activity/Fragment/DialogFrament等

可以看出,AutoLifecycle除了支持常規的生命週期,還能支持自定義的特殊生命週期,比如View Inflate前。

另外,上面都是以Activity爲例,不過顯然這套框架可以靈活擴展,不侷限於Activity,還能適用於Fragment、DialogFrament等。

總結

Lifecycle-Aware思想是Google官方提出來的概念:賦予普通類自動感知生命週期的能力。而本文也是基於這個思想,提供了一些具體實踐和優化的思路,讀者同學可以根據自己的情況做更多的改進和嘗試。

——————
wingjay
謝謝。

參考

https://developer.android.com/topic/libraries/architecture/lifecycle.html
https://www.atatech.org/articles/63098
https://github.com/trello/RxLifecycle
http://reactivex.io/RxJava/javadoc/rx/subjects/PublishSubject.html
http://reactivex.io/RxJava/javadoc/rx/Observable.Transformer.html

發佈了45 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章