【譯】Google官方推出的Android架構組件系列文章(三)處理生命周期

系列文章導航

  1. 【譯】Google官方推出的Android架構組件系列文章(一)App架構指南
  2. 【譯】Google官方推出的Android架構組件系列文章(二)將Architecture Components引入工程
  3. 【譯】Google官方推出的Android架構組件系列文章(三)處理生命周期
  4. 【譯】Google官方推出的Android架構組件系列文章(四)LiveData
  5. 【譯】Google官方推出的Android架構組件系列文章(五)ViewModel
  6. 【譯】Google官方推出的Android架構組件系列文章(六)Room持久化庫

原文地址:https://developer.android.com/topic/libraries/architecture/lifecycle.html

android.arch.lifecycle 包提供了類和接口允許你構建生命周期感知(lifecycle-aware)的組件——可以基于當前activity或fragment生命周期自動調節它們的行為的組件。

注意:將 android.arch.lifecycle 導入Android工程,請參見將Architecture Components引入工程

大部分定義在Android框架中的組件都包含附加到其上的生命周期。這些生命周期由操作系統或運行在你的進程中的框架代碼管理。它們是Android如何工作的核心,你的應用程序必須尊重它們。不這樣做可能會觸發內存泄漏甚至應用程序崩潰。

假設在屏幕上我們有一個展示設備位置的activity。一個常見的實現可能像下面這樣:

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();
    }
}

即使這個例子看起來不錯,在一個真正的應用中,你最終會遇到太多像這樣的調用,然后onStart()onStop()方法會變得非常大。

此外,一些組件不能剛剛在onStart()中啟動。如果我們需要在啟動位置觀察之前檢查一些配置怎么辦?有可能某些情況檢查是在activity已經停止之后才結束,這意味著myLocationListener.start()的調用是在myLocationListener.stop()調用之后,基本上就會一直保持連接了。

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();
    }
}

android.arch.lifecycle包提供了類和接口,幫助你以靈活和隔離的方式解決這些問題。

Lifecycle

Lifecycle類保存有組件(如activity或fragment)的生命周期狀態信息,并允許其他對象觀察此狀態。

Lifecycle使用兩個主要的枚舉來跟蹤其相關組件的生命周期狀態。

Event

從框架和Lifecycle類分派的生命周期事件。這些事件映射到activity和fragment的回調事件。

State

由Lifecycle對象跟蹤的組件的當前狀態
lifecycle-states.png

將狀態視作圖的結點,事件看作這些結點之間的邊。

一個類可以通過給方法添加注解來觀察組件的生命周期。

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

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
    }
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());

LifecycleOwner

LifecycleOwner 是一個單一方法接口,表示該類擁有一個Lifecycle。它的唯一方法getLifecycle()必須被這個類實現。

此類從單獨的類(比如activity和fragment)里抽象了Lifecycle的所有權,并允許編寫與兩者兼容的組件。任何自定義應用程序類可以實現LifecycleOwner接口。

注意:由于Architecture Components還處于Alpha階段,FragmentAppCompatActivity類不能實現它(因為我們不能從一個穩定的組件中添加一個依賴,導致其變成不穩定的API)。在Lifecycle穩定之前,為了方便,提供了LifecycleActivityLifecycleFragment類。在Lifecycle工程發布后,支持庫的fragment和activity將會實現LifecycleOwner接口。到時將不推薦使用LifecycleActivityLifecycleFragment。另請參閱在自定義activity和fragment中實現LifecycleOwner

對于上面的例子,我們可以讓MyLocationListener類成為LifecycleObserver,然后在onCreate中使用我們的Lifecycle初始化它。這允許MyLocationListener類是自足的,意味著它可以在必要的時候進行自我清理。

class MyActivity extends LifecycleActivity {
    private MyLocationListener myLocationListener;

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

一個常見的用例是,避免在Lifecycle處于不良狀態時調用某些回調。例如,如果回調在activity狀態保存后運行一個fragment事務,則會觸發崩潰,因此我們永遠不會想要調用那個回調。

為了簡化此用例,Lifecycle類允許其他對象查詢當前狀態。

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

通過這個實現,我們的LocationListener類是完全生命周期感知的了。它可以自行初始化和清理,而不受activity的管理。如果我們需要從另一個Activity或Fragment使用它,我們只需要初始化它。所有的安裝和拆卸操作都由類本身管理。

可以與Lifecycle一起使用的類被稱為生命周期感知組件。鼓勵那些提供需要使用Android生命周期的類的庫提供生命周期感知組件,以便客戶端可以輕松繼承這些類,而無需在客戶端手動進行生命周期管理。

LiveData是生命周期感知組件的示例。與ViewModel一起使用LiveData,可以在遵循Android生命周期的情況下,更容易使用數據填充UI。

Lifecycle最佳實踐

  • 保持UI控制器(activity和fragment)盡可能瘦。它們不應該視圖獲取自己的數據,而是使用ViewModel來做,并觀察LiveData以將更改反映到View中。
  • 嘗試別寫數據驅動的UI,你的UI控制器負責在數據更改時更新View,而不是將用戶操作通知回ViewModel。
  • 把你的數據邏輯放到ViewModel類中。ViewModel應該扮演你的UI控制器和應用其余部分之間的連接器。但是請注意,不是ViewModel的責任來拉取數據(比如,從網絡)。相反,ViewModel應該調用相應的組件來做這個事兒,然后把結果返回給UI控制器。
  • 使用Data Binding來保持view和UI控制器之間干凈的接口。這可以讓你的視圖更具聲明性,并最大限度減少你需要在activity和fragment之間編寫的更新代碼。如果你更喜歡Java中這么做,使用像ButterKnife這樣的庫來避免模板代碼,并進行更好的抽象。
  • 如果你的UI很復雜,考慮創建一個Presenter類來處理UI修改。這通常是過度的,但可能讓你的UI更容易測試。
  • 永遠不要在ViewModel中引用viewactivity context。如果ViewModel的生命周期超過activity(比如更改配置的情況),activity將會泄漏,無法正確地被垃圾回收。

附錄

自定義的activity和fragment實現LifecycleOwer

任何自定義的activity和fragment可以通過實現內置的LifecycleRegistryOwner接口(而不是擴展LifecycleFragmentLifecycleActivity)來轉換成LifecycleOwner

public class MyFragment extends Fragment implements LifecycleRegistryOwner {
    LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    @Override
    public LifecycleRegistry getLifecycle() {
        return lifecycleRegistry;
    }
}

如果你想把一個自定義類轉換成LifecycleOwner,你可以使用LifecycleRegistry類,但是你需要把事件轉發給這個類。對于實現了LifecycleRegistryOwner的activity和fragment來說,轉換將會自動完成。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容