讓普通 Java 類自動(dòng)感知 Activity Lifecycle

《億級(jí) Android 架構(gòu)》 地址:https://xiaozhuanlan.com/AndroidArch

背景

在 Android 開(kāi)發(fā)中,我們都很熟悉 Activity 的 Lifecycle,并且會(huì)在特定的 Lifecycle 下執(zhí)行特定的操作。當(dāng)然,我們清楚 Lifecycle 本身是帶有 Android 特質(zhì)的,那嘗試設(shè)想下,如果普通的 Java Class 也能自動(dòng)感知 Lifecycle 呢?咋一聽(tīng)這個(gè)想法似乎背后意義不大,但在實(shí)際探索中,我們發(fā)現(xiàn)這個(gè)特性能為我們達(dá)成一些之前未考慮到或者不易實(shí)現(xiàn)的優(yōu)化。

本文分享下我們基于這個(gè)思想所開(kāi)發(fā)的框架:AutoLifecycle 及其帶來(lái)的一些有意思的實(shí)踐。

  • 優(yōu)化一:當(dāng)Activity進(jìn)入onDestroy時(shí),自動(dòng)取消網(wǎng)絡(luò)請(qǐng)求返回
  • 優(yōu)化二:自動(dòng)將網(wǎng)絡(luò)請(qǐng)求時(shí)機(jī)提前到View渲染之前,提高頁(yè)面打開(kāi)速度
  • 優(yōu)化三:MVP改進(jìn),讓Presenter和View自動(dòng)bind/unBind

注:下文提到的Lifecycle-Aware就是這里指代的讓普通 Java Class 自動(dòng)獲取 Lifecycle。

實(shí)踐及優(yōu)化

優(yōu)化一:當(dāng)Activity進(jìn)入onDestroy時(shí),自動(dòng)取消網(wǎng)絡(luò)請(qǐng)求返回

在網(wǎng)絡(luò)請(qǐng)求時(shí),相信大家都有一個(gè)經(jīng)驗(yàn):在每個(gè)網(wǎng)絡(luò)結(jié)果回來(lái)時(shí),我們做的第一件事不是顯示數(shù)據(jù),而是寫(xiě)個(gè)if-else判斷Activity還在不在。

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

由于網(wǎng)絡(luò)請(qǐng)求都是異步的,所以不得不做這樣的判斷,來(lái)防止不可預(yù)測(cè)的空指針問(wèn)題或內(nèi)存泄漏問(wèn)題。

既然你總是擔(dān)心Activity還在不在,那么如果我們通過(guò)Lifecycle-Aware讓每個(gè)網(wǎng)絡(luò)請(qǐng)求能自動(dòng)感知Activity的onDestroy事件,
并在onDestroy時(shí),自動(dòng)把網(wǎng)絡(luò)請(qǐng)求結(jié)果取消掉不再返回,那就能夠消除這個(gè)擔(dān)憂了。

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

其中最關(guān)鍵的就是compose(bindUntilEvent(ActivityLifecycle.DESTROY))這句,它能達(dá)到的效果是:一旦Activity發(fā)生onDestroy時(shí),Observer的數(shù)據(jù)就會(huì)停止向Subscriber里流動(dòng)。從而保證onNext無(wú)需擔(dān)心ActivityDestroy這種情況。

在上面網(wǎng)絡(luò)請(qǐng)求的實(shí)踐里,你還可以根據(jù)自己的情況把Destroy換成Stop/Pause等,而且可以看出,這種自動(dòng)取消機(jī)制可適用于任何Observable,不僅僅是網(wǎng)絡(luò)請(qǐng)求。

優(yōu)化二:自動(dòng)將網(wǎng)絡(luò)請(qǐng)求提前到View Inflate之前,加速頁(yè)面渲染

先說(shuō)下這項(xiàng)優(yōu)化的原理。
通常,我們會(huì)在ActivityonCreate里依次執(zhí)行下面的代碼:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.XXX);   // Inflate View
    findViewByIds();                // 初始化View
    presenter.loadDataFromServer(); // 發(fā)起網(wǎng)絡(luò)請(qǐng)求
}

即在Inflate View初始化View之后,才發(fā)起網(wǎng)絡(luò)請(qǐng)求去加載數(shù)據(jù)。

而實(shí)際上,網(wǎng)絡(luò)請(qǐng)求是不占用主線程的,如果能在Inflate View之前就在其他線程發(fā)起網(wǎng)絡(luò)請(qǐng)求,可以把整個(gè)頁(yè)面顯示耗時(shí)縮短100ms-200ms。

LoadBeforeInflate優(yōu)化效果 (1).png

現(xiàn)在有了AutoLifecycle框架,我們就可以很輕松實(shí)現(xiàn):讓Presenter自動(dòng)監(jiān)聽(tīng)Inflate View這個(gè)生命周期,在那時(shí)發(fā)起網(wǎng)絡(luò)請(qǐng)求即可。

public class NewPresenter {

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

    // 當(dāng)Activity Inflate View前自動(dòng)回調(diào)
    @AutoLifecycleEvent(activity = ActivityLifecycle.PRE_INFLATE)
    private void onHostPreInflate() {
         loadDataFromServer(); // 發(fā)起網(wǎng)絡(luò)請(qǐng)求
    }
    ...
}

此時(shí),我們的Activity也不用手動(dòng)調(diào)用presenter.loadDataFromServer();了,因?yàn)镻resenter內(nèi)會(huì)在感知到Inflate View事件時(shí)自動(dòng)發(fā)起網(wǎng)絡(luò)請(qǐng)求。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.XXX);
    findViewByIds();
    // 無(wú)需手動(dòng)啟動(dòng)網(wǎng)絡(luò)請(qǐng)求
}

經(jīng)過(guò)測(cè)試,在保證單個(gè)網(wǎng)絡(luò)請(qǐng)求耗時(shí)相同的情況下,頁(yè)面從onCreate顯示數(shù)據(jù)的渲染耗時(shí)可以從550ms縮短到367ms,也就是30%-40%的優(yōu)化,效果是非常不錯(cuò)的,而且代碼也更加簡(jiǎn)潔清晰。

優(yōu)化前

優(yōu)化后
[圖片上傳失敗...(image-889271-1510146010290)]

通過(guò)簡(jiǎn)單的注冊(cè)AutoLifecycle,Presenter能夠自動(dòng)感知到所有Lifecycle,甚至包括自定義的特殊Lifecycle,如下圖:
[圖片上傳失敗...(image-8c0f0b-1510146010290)]

B2.png

優(yōu)化三:MVP改進(jìn),讓Presenter和View自動(dòng)bind/unBind

第一項(xiàng)優(yōu)化比較直接,可以先讓大家形成一個(gè)直觀印象。
我們項(xiàng)目是采用MVP項(xiàng)目,對(duì)于Presenter的使用存在一段固定代碼,即在onCreate時(shí)調(diào)用bindView(),在onDestroy時(shí)調(diào)用unBindView()。如下圖:

public class OldActivity extends BaseActivity {

    BasePresenter mPresenter = new BasePresenter();

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

    @Override
    protected void onDestroy() {
        mPresenter.unbindView(); // onDestroy時(shí)手動(dòng)unBindView
        super.onDestroy();
    }
}

那么,既然我們現(xiàn)在能讓一個(gè)普通類自動(dòng)感知Lifecycle,那其實(shí)也就能讓Presenter在感知到onCreate時(shí)自動(dòng)bindView,在感知到onDestroy時(shí)自動(dòng)unBindView。
改進(jìn)后的代碼如下:

public class NewActivity extends BaseActivity {

    NewPresenter mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = new NewPresenter(this); // 只需要?jiǎng)?chuàng)建即可
    }
}

public class NewPresenter {

    private IView mIView;

    public NewPresenter(IView iView) {
        this.mIView = iView;
        // 向AutoLifecycle注冊(cè)即可獲得Lifecycle回調(diào)
        AutoLifecycle.getInstance().init(this); 
    }

    // 當(dāng)Activity進(jìn)入onCreate后自動(dòng)調(diào)用
    @AutoLifecycleEvent(activity = ActivityLifecycle.CREATE)
    private void onHostCreate() {
        bindView(mIView); 
    }

    // 當(dāng)Activity進(jìn)入onDestroy后自動(dòng)調(diào)用
    @AutoLifecycleEvent(activity = ActivityLifecycle.DESTROY)
    private void onHostDestroy() {
        unBindView();
    }
}

其實(shí),在大家的平常開(kāi)發(fā)中,還會(huì)存在許多類似Presenter的類:需要在某個(gè)特定的Lifecycle下執(zhí)行一些動(dòng)作。這時(shí)就可以基于Lifecycle-Aware來(lái)讓這個(gè)普通類自動(dòng)去執(zhí)行,而不是去每個(gè)Activity/Fragment里寫(xiě)一遍,提高類的內(nèi)聚性。

AutoLifecycle的核心原理

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

1. 讓Activity對(duì)外發(fā)送Lifecycle事件

使用過(guò)RxJava的同學(xué)知道里面有一個(gè)PublishSubject,基于觀察者模式,主動(dòng)發(fā)送并接受消息。這里我們用PublishSubject來(lái)發(fā)送Lifecycle事件。見(jiàn)如下:

D1.png

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

D2.png

2. 感知某個(gè)Lifecycle的發(fā)生并自動(dòng)執(zhí)行回調(diào)

上面提了,PublishSubject不僅能發(fā)送消息,還能接受自己的消息?;谶@個(gè)特點(diǎn),我們便可以監(jiān)聽(tīng)每一個(gè)LifecycleEvent。如下圖:

D3.png

這里的參數(shù)Observable是我們希望被回調(diào)的函數(shù),IContextLifecycle是指定的Lifecycle。即當(dāng)指定的Lifecycle Event發(fā)生時(shí),會(huì)自動(dòng)subscribe提供的Observable。

基于這個(gè)功能,便可以實(shí)現(xiàn)上面場(chǎng)景一和場(chǎng)景二里的@AutoLifecycleEvent注解了,即把@AutoLifecycleEvent標(biāo)注的函數(shù)包裝成一個(gè)Observable,通過(guò)這個(gè)executeOn來(lái)注冊(cè)函數(shù)的執(zhí)行生命周期即可。

3. 監(jiān)聽(tīng)Lifecycle并取消網(wǎng)絡(luò)請(qǐng)求結(jié)果

在場(chǎng)景三里,我們?yōu)榫W(wǎng)絡(luò)請(qǐng)求的Observable提供了一個(gè)Transformer,它能在監(jiān)聽(tīng)到某個(gè)Lifecycle發(fā)生時(shí),停止數(shù)據(jù)流的向下流動(dòng)。該Transformer的核心實(shí)現(xiàn)是:

D4.png

可以看出,當(dāng)指定的Lifecycle一旦發(fā)生,我們網(wǎng)絡(luò)請(qǐng)求Observable就會(huì)停止向下傳遞數(shù)據(jù)。

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

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

另外,上面都是以Activity為例,不過(guò)顯然這套框架可以靈活擴(kuò)展,不局限于Activity,還能適用于Fragment、DialogFrament等。

總結(jié)

Lifecycle-Aware思想是Google官方提出來(lái)的概念:賦予普通類自動(dòng)感知生命周期的能力。而本文也是基于這個(gè)思想,提供了一些具體實(shí)踐和優(yōu)化的思路,讀者同學(xué)可以根據(jù)自己的情況做更多的改進(jìn)和嘗試。

——————
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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,745評(píng)論 25 709
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,910評(píng)論 2 59
  • 懶得處理樣式了, 將就著看吧. 官網(wǎng)地址: https://developer.android.com/topic...
    Reddington_604e閱讀 1,906評(píng)論 0 1
  • 哪種葉子的黃最能打動(dòng)你,對(duì)我來(lái)說(shuō),非銀杏莫屬了。是因?yàn)樗诙摘?dú)占芳華,是因?yàn)樗琼n劇浪漫色彩的主背景,說(shuō)不清道不...
    木木夕_73f6閱讀 629評(píng)論 0 3
  • 今天發(fā)現(xiàn)一個(gè)真相——原來(lái)我值得這世界上所有美好的東西。 今天的年終獎(jiǎng)對(duì)我沖擊力太大了,我第一次感受到我愛(ài)錢寶寶。我...
    清理放下閱讀 252評(píng)論 0 0

友情鏈接更多精彩內(nèi)容