【背上Jetpack之Lifecycle】萬物基于 Lifecycle 默默無聞大用處

系列文章

【背上Jetpack】Jetpack 主要組件的依賴及傳遞關(guān)系

【背上Jetpack】AdroidX下使用Activity和Fragment的變化

【背上Jetpack之Fragment】你真的會用Fragment嗎?Fragment常見問題以及androidx下Fragment的使用新姿勢

【背上Jetpack之Fragment】從源碼角度看 Fragment 生命周期 AndroidX Fragment1.2.2源碼分析

【背上Jetpack之OnBackPressedDispatcher】Fragment 返回棧預(yù)備篇

【背上Jetpack之Fragment】從源碼的角度看Fragment 返回棧 附多返回棧demo

【背上Jetpack】絕不丟失的狀態(tài) androidx SaveState ViewModel-SaveState 分析

【背上Jetpack之ViewModel】即使您不使用MVVM也要了解ViewModel ——ViewModel 的職能邊界

目錄

前言

Android 中有一個比較重要的概念:「生命周期」笼裳。剛畢業(yè)去面試,總會被問到「四大組件的生命周期」這類的問題佛舱。17年的 IO 大會上诸尽,Google 推出了 Lifecycle-Aware Components(生命周期感知組件)原杂,幫助開發(fā)者組織更好,更輕量弦讽,易于維護(hù)的代碼

本文介紹 Lifecycle 的職責(zé)以及簡單分析 lifecycle 如何感知 activity 和 fragment 污尉,幫助您對 Lifecycle 有一個感性的認(rèn)識

萬物基于 Lifecycle

手動管理生命周期的痛苦你不懂

lifecycles

魯迅曾說過:萬物基于 Lifecycle

image

哦不對

image

Android 中的視圖控制器就有這么多生命周期的情況,所以處理好生命周期十分重要往产,否則會導(dǎo)致內(nèi)存泄漏甚至是程序崩潰被碗。這里引用 官方文檔 的例子

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

    void start() {
        // 連接系統(tǒng)的定位服務(wù)
    }

    void stop() {
        // 與系統(tǒng)的定位服務(wù)斷開連接
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

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

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        //管理其他需要響應(yīng) activity 生命周期的組件
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        //管理其他需要響應(yīng) activity 生命周期的組件
    }
}

此示例看起來不錯,在實際的應(yīng)用程序中仿村,您仍然會響應(yīng)生命周期的當(dāng)前狀態(tài)而進(jìn)行過多的調(diào)用來管理 UI 和其他組件锐朴。 管理多個組件會在生命周期方法中放置大量代碼,例如 onStart() 和 onStop()蔼囊,這使它們難以維護(hù)

而且焚志,不能保證組件在 activity 或 fragment 停止之前就已啟動。 如果我們需要執(zhí)行長時間運行的操作(例如onStart() 中的某些配置檢查)畏鼓,則可能會導(dǎo)致爭用情況酱酬,其中onStop() 方法在 onStart() 之前完成,從而使組件的生存期超過了所需的生存期云矫。

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

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

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // 如果在 activity 停止后調(diào)用此回調(diào)怎么辦膳沽?
            if (result) {
                myLocationListener.start();
            }
        });
    }

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

如果有所有的組件,都能感知外部的生命周期让禀,能在相應(yīng)的時機釋放資源挑社,并且在錯過生命周期時能及時叫停異步的任務(wù)就好了,

image

我們不妨先思考一下巡揍,如果實現(xiàn)這樣的想法痛阻,應(yīng)該如何做

按照慣例的思考

首先我們先來整理一下我們的需求

  • 內(nèi)部組件能夠感知外部的生命周期
  • 能夠統(tǒng)一地管理,做到一處修改腮敌,處處生效
  • 能夠及時叫停錯過的任務(wù)

針對需求1阱当,可以用觀察者模式,內(nèi)部組件能夠在外部生命周期變化時做出相應(yīng)

針對需求2糜工,可以將依賴組件的代碼移出生命周期方法內(nèi)斗这,然后移入組件本身,這樣只需修改組件內(nèi)部邏輯即可

針對需求3啤斗,可以在合適的時機移除觀察者

觀察者模式

關(guān)于開發(fā)者模式,我第一次比較詳細(xì)的了解是在 扔物線給 Android 開發(fā)者的 RxJava 詳解赁咙。

觀察者模式面向的需求是:A 對象(觀察者)對 B 對象(被觀察者)的某種變化高度敏感钮莲,需要在 B 變化的一瞬間做出反應(yīng)荧止。舉個例子怔鳖,新聞里喜聞樂見的警察抓小偷,警察需要在小偷伸手作案的時候?qū)嵤┳ゲ丁T谶@個例子里杏愤,警察是觀察者,小偷是被觀察者蒲凶,警察需要時刻盯著小偷的一舉一動熟妓,才能保證不會漏過任何瞬間。程序的觀察者模式和這種真正的『觀察』略有不同慈俯,觀察者不需要時刻盯著被觀察者(例如 A 不需要每過 2ms 就檢查一次 B 的狀態(tài))渤刃,而是采用注冊(Register)或者稱為訂閱(Subscribe)的方式,告訴被觀察者:我需要你的某某狀態(tài)贴膘,你要在它變化的時候通知我卖子。 Android 開發(fā)中一個比較典型的例子是點擊監(jiān)聽器 OnClickListener 。對設(shè)置 OnClickListener 來說刑峡, View 是被觀察者洋闽, OnClickListener 是觀察者,二者通過 setOnClickListener() 方法達(dá)成訂閱關(guān)系突梦。訂閱之后用戶點擊按鈕的瞬間诫舅,Android Framework 就會將點擊事件發(fā)送給已經(jīng)注冊的 OnClickListener 。采取這樣被動的觀察方式宫患,既省去了反復(fù)檢索狀態(tài)的資源消耗刊懈,也能夠得到最高的反饋速度。當(dāng)然撮奏,這也得益于我們可以隨意定制自己程序中的觀察者和被觀察者俏讹,而警察叔叔明顯無法要求小偷『你在作案的時候務(wù)必通知我』。

OnClickListener 的模式大致如下圖:

image

上述描述及圖片均來自 給 Android 開發(fā)者的 RxJava 詳解

因此在生命周期組件的生命周期發(fā)生變化時告訴觀察者畜吊,內(nèi)部組件即可感知外部的生命周期

引入 Lifecycle 后

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

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void disconnectListener() {
        ...
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

源碼結(jié)構(gòu)

image

這是 Lifecycle 的結(jié)構(gòu)泽疆,抽象類,其內(nèi)部有兩個枚舉玲献,分別代表著「事件」和「狀態(tài)」殉疼,此外還有三個方法,添加/移除觀察者捌年,獲取當(dāng)前狀態(tài)

注意瓢娜,這里 State 中的枚舉順序是有意義的,后文詳細(xì)介紹

其實現(xiàn)類為 LifecycleRegistry 礼预,可以處理多個觀察者

LifecycleRegistry

其內(nèi)部持有當(dāng)前的狀態(tài) mState 眠砾,LifecycleOwner 以及觀察者的自定義列表,同時重寫了父類的添加/刪除觀察者的方法

LifecycleOwner

LifecycleOwner 托酸,具有 Android 的生命周期褒颈,定制組件可以使用這些事件來處理生命周期更改柒巫,而無需在 Activity 或 Fragment 中實現(xiàn)任何代碼

LifecycleObserver ,將一個類標(biāo)記為 LifecycleObserver谷丸。 它沒有任何方法堡掏,而是依賴于 OnLifecycleEvent 注解的方法

LifecycleEventObserver ,可以接收任何生命周期更改并將其分派給接收方刨疼。

如果一個類實現(xiàn)此接口并同時使用 OnLifecycleEvent泉唁,則注解將被忽略

DefaultLifecycleObserver ,用于監(jiān)聽 LifecycleOwner 狀態(tài)更改的回調(diào)接口揩慕。

如果一個類同時實現(xiàn)了此接口和 LifecycleEventObserver亭畜,則將首先調(diào)用DefaultLifecycleObserver 的方法,然后再調(diào)用LifecycleEventObserver.onStateChanged(LifecycleOwner漩绵,Lifecycle.Event)

注意:使用 DefaultLifecycleObserver 需引入

implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

簡單的源碼分析

activity 生命周期處理

首先我們還是來看 androidx.activity.ComponentActivity 贱案,這個類我們這個系列的文章里提到多次,第一次提及是在 【背上Jetpack】絕不丟失的狀態(tài) androidx SaveState ViewModel-SaveState 分析 止吐,感興趣的小伙伴可以看看宝踪。

ComponentActivity

其實現(xiàn)的接口大多數(shù)我們都已經(jīng)探討過了,今天我們來看看 LifecycleOwner

ActivityResultCaller 為 activity 1.2.0-alpha02 推出的碍扔,旨在統(tǒng)一 onActivityResult 瘩燥,這里暫時不討論它

image

既然實現(xiàn)了 LifecycleOwner 接口,必定重寫 getLifecycle() 方法

// androidx.activity.ComponentActivity.java
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

@Override
public Lifecycle getLifecycle() {
    return mLifecycleRegistry;
}

其返回的 Lifecycle 為 實現(xiàn)類 LifecycleRegistry 的實例

而 activity 操作生命周期是通過 ReportFragment 處理的

// androidx.activity.ComponentActivity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ReportFragment.injectIfNeededIn(this);
    //...
}

// ReportFragment
public static void injectIfNeededIn(Activity activity) {
    if (Build.VERSION.SDK_INT >= 29) {
        // api 29 及以上 直接注冊正確的生命周期回調(diào)
        activity.registerActivityLifecycleCallbacks(
                new LifecycleCallbacks());
    }
    android.app.FragmentManager manager = activity.getFragmentManager();
    if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
        manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
        manager.executePendingTransactions();
    }
}
image
// ReportFragment.java
static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
    if (activity instanceof LifecycleRegistryOwner) {
        ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
        return;
    }
    if (activity instanceof LifecycleOwner) {
        Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
        if (lifecycle instanceof LifecycleRegistry) {
            ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
        }
    }
}

private void dispatch(@NonNull Lifecycle.Event event) {
    if (Build.VERSION.SDK_INT < 29) {
        dispatch(getActivity(), event);
    }
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    dispatch(Lifecycle.Event.ON_CREATE);
}
@Override
public void onStart() {
    super.onStart();
    dispatch(Lifecycle.Event.ON_START);
}
@Override
public void onResume() {
    super.onResume();
    dispatch(Lifecycle.Event.ON_RESUME);
}
@Override
public void onPause() {
    super.onPause();
    dispatch(Lifecycle.Event.ON_PAUSE);
}
@Override
public void onStop() {
    super.onStop();
    dispatch(Lifecycle.Event.ON_STOP);
}
@Override
public void onDestroy() {
    super.onDestroy();
    dispatch(Lifecycle.Event.ON_DESTROY);
}
// LifecycleCallbacks
static class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
    @Override
    public void onActivityPostCreated(@NonNull Activity activity,
            @Nullable Bundle savedInstanceState) {
        dispatch(activity, Lifecycle.Event.ON_CREATE);
    }

    @Override
    public void onActivityPostStarted(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_START);
    }

    @Override
    public void onActivityPostResumed(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_RESUME);
    }
    @Override
    public void onActivityPrePaused(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_PAUSE);
    }

    @Override
    public void onActivityPreStopped(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_STOP);
    }

    @Override
    public void onActivityPreDestroyed(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_DESTROY);
    }
    //...
}

在 activity 的 onCreate 方法中不同,調(diào)用了 ReportFragment 中的靜態(tài)方法 injectIfNeededIn() 厉膀。而其內(nèi)部,如果 api 29 及以上的設(shè)備上直接注冊正確的生命周期回調(diào)二拐,低版本通過啟動 ReportFragment 服鹅,借助 fragment 各個生命周期來處理生命周期回調(diào)

fragment 生命周期處理

在 fragment 內(nèi)部,每個生命周期節(jié)點調(diào)用 handleLifecycleEvent 方法

// Fragment.java
public Fragment() {
    initLifecycle();
}

private void initLifecycle() {
    mLifecycleRegistry = new LifecycleRegistry(this);
}

@Override
public Lifecycle getLifecycle() {
    return mLifecycleRegistry;
}

void performCreate(Bundle savedInstanceState) {
    onCreate(savedInstanceState);
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);    
}

void performStart() {
    onStart();
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}

void performResume() {
    onResume();
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);    
}

void performPause() {
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
    onPause();
}

void performStop() {
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
    onStop();
}

void performDestroy() {
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    onDestroy();
}

Lifecycle State 大小比較

Lifecycle.State 中有一個 isAtLeast 方法百新,用于判斷當(dāng)前狀態(tài)是否不小于傳入的狀態(tài)

// Lifecycle.State
public boolean isAtLeast(@NonNull State state) {
    return compareTo(state) >= 0;
}

枚舉的 compareTo 方法其實是比較的枚舉聲明的順序

而 State 的順序為 DESTROYED -> INITIALIZED -> CREATED -> STARTED -> RESUMED

如果傳入的 state 為 STARTED企软,則當(dāng)前狀態(tài)為 STARTED 或 RESUMED 時返回 true ,否則返回 false

LiveData 篇會用到這個知識點


關(guān)于我


我是 Fly_with24

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饭望,一起剝皮案震驚了整個濱河市仗哨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铅辞,老刑警劉巖厌漂,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異斟珊,居然都是意外死亡苇倡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雏节,“玉大人胜嗓,你說我怎么就攤上這事」痴В” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵怔锌,是天一觀的道長寥粹。 經(jīng)常有香客問我,道長埃元,這世上最難降的妖魔是什么涝涤? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮岛杀,結(jié)果婚禮上阔拳,老公的妹妹穿的比我還像新娘。我一直安慰自己类嗤,他們只是感情好糊肠,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著遗锣,像睡著了一般货裹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上精偿,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天弧圆,我揣著相機與錄音,去河邊找鬼笔咽。 笑死搔预,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叶组。 我是一名探鬼主播拯田,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扶叉!你這毒婦竟也來了勿锅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤枣氧,失蹤者是張志新(化名)和其女友劉穎溢十,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體达吞,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡张弛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吞鸭。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡寺董,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刻剥,到底是詐尸還是另有隱情遮咖,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布造虏,位于F島的核電站御吞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏漓藕。R本人自食惡果不足惜陶珠,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望享钞。 院中可真熱鬧揍诽,春花似錦、人聲如沸栗竖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽划滋。三九已至饵筑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間处坪,已是汗流浹背根资。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留同窘,地道東北人玄帕。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像想邦,于是被迫代替她去往敵國和親裤纹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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