背景
在 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
無需擔心Activity
已Destroy
這種情況。
在上面網絡請求的實踐裏,你還可以根據自己的情況把Destroy
換成Stop
/Pause
等,而且可以看出,這種自動取消機制可適用於任何Observable
,不僅僅是網絡請求。
優化二:自動將網絡請求提前到View Inflate之前,加速頁面渲染
先說下這項優化的原理。
通常,我們會在Activity
的onCreate
裏依次執行下面的代碼:
@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
。
現在有了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)]
通過簡單的註冊AutoLifecycle
,Presenter
能夠自動感知到所有Lifecycle
,甚至包括自定義的特殊Lifecycle
,如下圖:
[圖片上傳失敗...(image-8c0f0b-1510146010290)]
優化三: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事件。見如下:
這裏的Lifecycle事件可以自己定義,比如前面提到的PRE_INFLATE
事件,是在setContentView之前發送,類似:
2. 感知某個Lifecycle的發生並自動執行回調
上面提了,PublishSubject
不僅能發送消息,還能接受自己的消息。基於這個特點,我們便可以監聽每一個LifecycleEvent。如下圖:
這裏的參數Observable是我們希望被回調的函數,IContextLifecycle是指定的Lifecycle。即當指定的Lifecycle Event發生時,會自動subscribe提供的Observable。
基於這個功能,便可以實現上面場景一和場景二里的@AutoLifecycleEvent
註解了,即把@AutoLifecycleEvent
標註的函數包裝成一個Observable,通過這個executeOn
來註冊函數的執行生命週期即可。
3. 監聽Lifecycle並取消網絡請求結果
在場景三裏,我們爲網絡請求的Observable
提供了一個Transformer
,它能在監聽到某個Lifecycle發生時,停止數據流的向下流動。該Transformer
的核心實現是:
可以看出,當指定的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