理解Android Architecture Components系列之Lifecycle(三)

有了整體的認(rèn)識(shí)缸废,就可以對(duì)之前沒有詳細(xì)介紹的類做一個(gè)深入的探究仅仆。首先來看看Lifecycle。

Handling Lifecycles

android.arch.lifecycle提供的類和接口可以讓你構(gòu)建能感知生命周期(lifecycle-aware)的類兜畸。所謂可以感知生命周期就是能夠根據(jù)Activity或者Fragment的生命周期自行調(diào)整類的行為梧油。

Android系統(tǒng)中定義的大多數(shù)組件都是有生命周期的。這些組件的生命周期是由系統(tǒng)管理的夹姥。作為一個(gè)開發(fā)者必須遵守這些生命周期的規(guī)則杉武,否則就會(huì)出現(xiàn)內(nèi)存泄漏或者應(yīng)用崩潰的情況。

這么說好像有點(diǎn)無力啊辙售,讓我們一起舉個(gè)例子吧轻抱。比如我們需要在Activity中做一個(gè)對(duì)位置的監(jiān)聽:

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
  }

    public void onStart() {
        super.onStart();
        myLocationListener.start();
    }

    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

在Activity的onCreate()方法中創(chuàng)建Listener,在onStart()時(shí)開始監(jiān)聽旦部,在onStop()中停止監(jiān)聽祈搜。看起來一切正常啊士八,就是可能Activty不停的切換會(huì)調(diào)用多次LocationListener的onStartonStop而已容燕。

但是問題來了,舉個(gè)例子哈婚度。比如有些App會(huì)提供一個(gè)選項(xiàng)缰趋,說允不允許提供位置,那么在定位之前就需要檢查這些設(shè)置陕见。好,那么就在Activity里提供檢查的代碼:

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

看起來好像也沒有問題呀秘血,讓我們來一起分析一下這段代碼。假如程序執(zhí)行到super.onStart()這里還沒等checkUserStatus執(zhí)行完成就執(zhí)行了onStop()(這里就假設(shè)檢查本身回花費(fèi)時(shí)間比較長吧)评甜,意識(shí)到出了什么問題了嗎灰粮?myLocationListenerstop()方法在start()之前執(zhí)行了,這本身在邏輯上就是不合理的忍坷。更嚴(yán)重的問題是:myLocationListener過一段時(shí)間開始執(zhí)行start()粘舟,然后沒有stop的代碼被調(diào)用了。這意味著如果沒有其他操作佩研,Activity將會(huì)一直在后臺(tái)監(jiān)聽Location的改變柑肴。

Lifecycle就是用來解決這些問題的,并且解決的非常優(yōu)雅(in a resilient and isolated way)旬薯。

Lifecycle

Lifecycle是一個(gè)包含組件(Activity或者Fragment)生命周期狀態(tài)的類晰骑,這個(gè)類還能夠?yàn)槠渌愄峁┊?dāng)前的生命周期。

Lifecycle使用兩個(gè)主要的枚舉來跟蹤他所關(guān)聯(lián)組件的生命周期绊序。

  • Event 事件 從組件或者Lifecycle類分發(fā)出來的生命周期硕舆,它們和Activity/Fragment生命周期的事件一一對(duì)應(yīng)秽荞。(ON_CREATE,ON_START,ON_RESUME,ON_PAUSE,ON_STOP,ON_DESTROY)
  • State 狀態(tài) 當(dāng)前組件的生命周期狀態(tài)(INITIALIZED,DESTROYED,CREATED,STARTED,RESUMED)

說的有點(diǎn)抽象了,看一下下面的圖:

image.png

具體的代碼如下:

public class MyObserver implements LifecycleObserver {
   @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
   public void onResume() {
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
   public void onPause() {
   }
}
//aLifecycleOwner一般是實(shí)現(xiàn)了LifecycleOwner的類抚官,比如Activity/Fragment
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());

LifecycleOwner

那什么是LifecycleOwner呢扬跋?實(shí)現(xiàn)LifecycleOwner就表示這是個(gè)有生命周期的類,他有一個(gè)getLifecycle ()方法是必須實(shí)現(xiàn)的凌节。com.android.support:appcompat-v7:26.1.0中的AppCompatActivity已經(jīng)實(shí)現(xiàn)了這個(gè)接口钦听,詳細(xì)的實(shí)現(xiàn)可以自行查看代碼。

對(duì)于前面提到的監(jiān)聽位置的例子倍奢∑由希可以把MyLocationListener實(shí)現(xiàn)LifecycleObserver,然后在LifecycleActivity/Fragment)的onCreate方法中初始化。這樣MyLocationListener就能自行處理生命周期帶來的問題娱挨。具體代碼如下:

class MyActivity extends AppCompatActivity {
   private MyLocationListener myLocationListener;

   public void onCreate(...) {
       myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
           // update UI
       });
       Util.checkUserStatus(result -> {
           if (result) {
               myLocationListener.enable();
           }
       });
 }
}

class MyLocationListener implements LifecycleObserver {
   private boolean enabled = false;
   public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
      ...
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_START)
   void start() {
       if (enabled) {
          // connect
       }
   }

   public void enable() {
       enabled = true;
       if (lifecycle.getState().isAtLeast(STARTED)) {
           // connect if not connected
       }
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
   void stop() {
       // disconnect if connected
   }
}

簡單過一下上面的代碼不難發(fā)現(xiàn)這樣可以解決文章開始提到的問題余指。(調(diào)用enable()時(shí)捕犬,已經(jīng)處于CREATED狀態(tài)跷坝,下面的代碼是不會(huì)執(zhí)行的)這樣在如果Lifecycle沒有處在合適的時(shí)機(jī)是不會(huì)調(diào)用相關(guān)回調(diào)的。現(xiàn)在MyLocationListener是有生命周期感知能力的(lifecycle-aware),不再依賴Activity的生命周期碉碉。

Lifecycles的最佳建議

  • 保持UI Controllers(Activity/Fragment)中代碼足夠簡潔柴钻。一定不能包含如何獲取數(shù)據(jù)的代碼,要通過ViewModel獲取LiveData形式的數(shù)據(jù)垢粮。
  • 用數(shù)據(jù)驅(qū)動(dòng)UI贴届,UI的職責(zé)就是根據(jù)數(shù)據(jù)改變顯示的內(nèi)容,并且把用戶操作UI的行為傳遞給ViewModel蜡吧。
  • 把業(yè)務(wù)邏輯相關(guān)的代碼放到ViewModel中毫蚓,把ViewModel看成是鏈接UI和App其他部分的膠水。但ViewModel不能直接獲取數(shù)據(jù)昔善,要通過調(diào)用其他類來獲取數(shù)據(jù)元潘。
  • 使用DataBinding來簡化View(布局文件)和UI Controllers(Activity/Fragment)之間的代碼
  • 如果布局本身太過復(fù)雜,可以考慮創(chuàng)建一個(gè)Presenter類來處理UI相關(guān)的改變君仆。雖然這么做會(huì)多寫很多代碼翩概,但是對(duì)于保持UI的簡介和可測試性是有幫助的。
  • 不要在ViewModel中持有任何View/Activity的context返咱。否則會(huì)造成內(nèi)存泄露钥庇。

附加說明

在 Support Library 26.1.0中Activity/Fragment已經(jīng)實(shí)現(xiàn)了LifecycleOwner接口。
如果想在自定義的類中實(shí)現(xiàn)LifecyclerOwner咖摹,就需要用到LifecycleRegistry,并且需要自行發(fā)送Event评姨。

相關(guān)文章:
理解Android Architecture Components系列(一)
理解Android Architecture Components系列(二)
理解Android Architecture Components系列之Lifecycle(三)
理解Android Architecture Components系列之LiveData(四)
理解Android Architecture Components系列之ViewModel(五)
理解Android Architecture Components系列之Room(六)
理解Android Architecture Components系列之Paging Library(七)
理解Android Architecture Components系列之WorkManager(八)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市萤晴,隨后出現(xiàn)的幾起案子参咙,更是在濱河造成了極大的恐慌龄广,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕴侧,死亡現(xiàn)場離奇詭異择同,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)净宵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門敲才,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人择葡,你說我怎么就攤上這事紧武。” “怎么了敏储?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵阻星,是天一觀的道長。 經(jīng)常有香客問我已添,道長妥箕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任更舞,我火速辦了婚禮畦幢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缆蝉。我一直安慰自己宇葱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布刊头。 她就那樣靜靜地躺著黍瞧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪原杂。 梳的紋絲不亂的頭發(fā)上印颤,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音污尉,去河邊找鬼膀哲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛被碗,可吹牛的內(nèi)容都是我干的某宪。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼锐朴,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼兴喂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤衣迷,失蹤者是張志新(化名)和其女友劉穎畏鼓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壶谒,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年汗菜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了让禀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陨界,死狀恐怖巡揍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情菌瘪,我是刑警寧澤腮敌,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站俏扩,受9級(jí)特大地震影響糜工,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜动猬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一啤斗、第九天 我趴在偏房一處隱蔽的房頂上張望表箭。 院中可真熱鬧赁咙,春花似錦、人聲如沸免钻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽极舔。三九已至凤覆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拆魏,已是汗流浹背盯桦。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渤刃,地道東北人拥峦。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像卖子,于是被迫代替她去往敵國和親略号。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容