Android Jetpack架構(gòu)篇:Lifecycles

Android Jetpack架構(gòu)篇:Lifecycles

使用生命周期感知組件處理生命周期

生命周期感知組件執(zhí)行操作以響應(yīng)另一個(gè)組件(例如活動(dòng)和片段)的生命周期狀態(tài)的更改。這些組件可幫助您生成更易于組織且通常更輕量級(jí)的代碼,這些代碼更易于維護(hù)寸认。

一種常見的模式是在活動(dòng)和片段的生命周期方法中實(shí)現(xiàn)依賴組件的操作。但是盾戴,這種模式導(dǎo)致代碼組織不良以及錯(cuò)誤的增加油吭。通過使用生命周期感知組件勾扭,您可以將依賴組件的代碼移出生命周期方法并移入組件本身矫钓。

該android.arch.lifecycle 包提供了類和接口浮禾,使您可以構(gòu)建生命周期感知 組件 - 這些組件可以根據(jù)活動(dòng)或片段的當(dāng)前生命周期狀態(tài)自動(dòng)調(diào)整其行為。

注意: 要導(dǎo)入 android.arch.lifecycle Android項(xiàng)目份汗,請(qǐng)參閱向項(xiàng)目添加組件

Android框架中定義的大多數(shù)應(yīng)用程序組件都附加了生命周期蝴簇。生命周期由操作系統(tǒng)或流程中運(yùn)行的框架代碼管理杯活。它們是Android工作原理的核心,您的應(yīng)用程序必須尊重它們熬词。不這樣做可能會(huì)觸發(fā)內(nèi)存泄漏甚至應(yīng)用程序崩潰旁钧。

想象一下,我們有一個(gè)活動(dòng)互拾,在屏幕上顯示設(shè)備位置歪今。常見的實(shí)現(xià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;

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

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

即使這個(gè)示例看起來很好,但在真實(shí)的應(yīng)用程序中颜矿,最終會(huì)有太多的調(diào)用來管理UI和其他組件以響應(yīng)生命周期的當(dāng)前狀態(tài)寄猩。管理多個(gè)組件會(huì)在生命周期方法中放置大量代碼,例如 onStart()onStop()骑疆,這使得它們難以維護(hù)田篇。

此外,無法保證組件在活動(dòng)或片段停止之前啟動(dòng)箍铭。如果我們需要執(zhí)行長(zhǎng)時(shí)間運(yùn)行的操作(例如某些配置檢入)泊柬,則尤其如此onStart()。這可能導(dǎo)致競(jìng)爭(zhēng)條件诈火,其中onStop()方法在之前完成onStart()兽赁,使組件保持活動(dòng)的時(shí)間長(zhǎng)于其所需的時(shí)間。

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

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

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

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

android.arch.lifecycle 軟件包提供了類和接口冷守,可幫助您以彈性和隔離的方式解決這些問題刀崖。

Lifecycle

Lifecycle 是一個(gè)類,它包含有關(guān)組件生命周期狀態(tài)的信息(如活動(dòng)或片段)拍摇,并允許其他對(duì)象觀察此狀態(tài)蒲跨。

Lifecycle 使用兩個(gè)主要枚舉來跟蹤其關(guān)聯(lián)組件的生命周期狀態(tài):

Event(事件)

從框架和Lifecycle類調(diào)度的生命周期事件 。這些事件映射到活動(dòng)和片段中的回調(diào)事件授翻。

State(狀態(tài))

Lifecycle對(duì)象跟蹤的組件的當(dāng)前狀態(tài) 或悲。

Alt text

將狀態(tài)視為圖形的節(jié)點(diǎn)孙咪,將事件視為這些節(jié)點(diǎn)之間的邊緣。

類可以通過向其方法添加注釋來監(jiān)視組件的生命周期狀態(tài)巡语。然后翎蹈,您可以通過調(diào)用 類的 addObserver() 方法Lifecycle并傳遞觀察者的實(shí)例來添加觀察者,如以下示例所示:

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

在上面的示例中男公,myLifecycleOwner對(duì)象實(shí)現(xiàn)了 LifecycleOwner 接口荤堪,將在下一節(jié)中進(jìn)行說明。

LifecycleOwner

LifecycleOwner 是一個(gè)單一的方法接口枢赔,表示該類有一個(gè) Lifecycle澄阳。它有一個(gè)方法, getLifecycle() 必須由類實(shí)現(xiàn)踏拜。如果您正在嘗試管理整個(gè)應(yīng)用程序流程的生命周期碎赢,請(qǐng)參閱 ProcessLifecycleOwner

此接口Lifecycle從各個(gè)類(例如Fragment和)中抽象出所有權(quán) AppCompatActivity速梗,并允許編寫與其一起使用的組件肮塞。任何自定義應(yīng)用程序類都可以實(shí)現(xiàn)LifecycleOwner 接口。

與實(shí)現(xiàn)的組件LifecycleObserver 無縫實(shí)現(xiàn) 工作的組件姻锁, LifecycleOwner 因?yàn)樗姓呖梢蕴峁┥芷谡碚裕^察者可以注冊(cè)觀察。

對(duì)于位置跟蹤的例子位隶,我們可以使MyLocationListener類實(shí)現(xiàn)LifecycleObserver 拷窜,然后用活動(dòng)的初始化 LifecycleonCreate()方法。這允許 MyLocationListener類自給自足涧黄,這意味著響應(yīng)生命周期狀態(tài)變化的邏輯被聲明MyLocationListener而不是活動(dòng)装黑。使各個(gè)組件存儲(chǔ)自己的邏輯使得活動(dòng)和片段邏輯更易于管理。

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

一個(gè)常見的用例是弓熏,如果Lifecycle現(xiàn)在不處于良好狀態(tài)恋谭,則應(yīng)避免調(diào)用某些回調(diào) 。例如挽鞠,如果回調(diào)在保存活動(dòng)狀態(tài)后運(yùn)行片段事務(wù)疚颊,則會(huì)觸發(fā)崩潰,因此我們永遠(yuǎn)不會(huì)想要調(diào)用該回調(diào)信认。

為了簡(jiǎn)化這個(gè)用例材义,Lifecycle該類允許其他對(duì)象查詢當(dāng)前狀態(tài)。

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.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

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

通過這種實(shí)現(xiàn)嫁赏,我們的LocationListener類完全可以識(shí)別生命周期其掂。如果我們需要使用LocationListener另一個(gè)活動(dòng)或片段,我們只需要初始化它潦蝇。所有設(shè)置和拆卸操作都由類本身管理款熬。

如果庫提供了需要使用Android生命周期的類深寥,我們建議您使用生命周期感知組件。您的庫客戶端可以輕松地集成這些組件贤牛,而無需在客戶端進(jìn)行手動(dòng)生命周期管理惋鹅。

實(shí)現(xiàn)自定義LifecycleOwner

支持庫26.1.0及更高版本中的片段和活動(dòng)已實(shí)現(xiàn)該LifecycleOwner 接口。

如果您要?jiǎng)?chuàng)建自定義類殉簸,則 LifecycleOwner可以使用 LifecycleRegistry 類闰集,但需要將事件轉(zhuǎn)發(fā)到該類,如以下代碼示例所示:

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry mLifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLifecycleRegistry = new LifecycleRegistry(this);
        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

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

生命周期感知組件的最佳實(shí)踐

  • 保持UI控制器(活動(dòng)和片段)盡可能精簡(jiǎn)般卑。他們不應(yīng)該試圖獲取自己的數(shù)據(jù); 相反武鲁,使用a ViewModel執(zhí)行此操作,并觀察LiveData 對(duì)象以將更改反映回視圖蝠检。

  • 嘗試編寫數(shù)據(jù)驅(qū)動(dòng)的UI沐鼠,您的UI控制器負(fù)責(zé)在數(shù)據(jù)更改時(shí)更新視圖,或者將用戶操作通知給 ViewModel蝇率。

  • 把你的數(shù)據(jù)邏輯放在你的 ViewModel班級(jí)。 ViewModel應(yīng)該作為UI控制器和應(yīng)用程序其余部分之間的連接器刽沾。但要小心本慕,ViewModel獲取數(shù)據(jù)(例如,從網(wǎng)絡(luò))是沒有 責(zé)任的侧漓。相反锅尘, ViewModel應(yīng)該調(diào)用適當(dāng)?shù)慕M件來獲取數(shù)據(jù),然后將結(jié)果提供回UI控制器布蔗。

  • 使用數(shù)據(jù)綁定來維護(hù)視圖和UI控制器之間的干凈界面藤违。這使您可以使視圖更具聲明性,并最大限度地減少在活動(dòng)和片段中編寫所需的更新代碼纵揍。如果您更喜歡使用Java編程語言執(zhí)行此操作顿乒,請(qǐng)使用Butter Knife之類的庫 來避免樣板代碼并具有更好的抽象。

  • 如果您的UI很復(fù)雜泽谨,請(qǐng)考慮創(chuàng)建一個(gè) presenter 類來處理UI修改璧榄。這可能是一項(xiàng)艱巨的任務(wù),但它可以使您的UI組件更容易測(cè)試吧雹。

  • 避免引用 你的內(nèi)容ViewActivity上下文ViewModel骨杂。如果ViewModel活動(dòng)超過活動(dòng)(在配置更改的情況下),您的活動(dòng)將泄漏并且垃圾收集器未正確處理雄卷。

生命周期感知組件的用例

生命周期感知組件可以使您在各種情況下更輕松地管理生命周期搓蚪。一些例子是:

  • 在粗粒度和細(xì)粒度位置更新之間切換。使用生命周期感知組件可在您的位置應(yīng)用程序可見時(shí)啟用細(xì)粒度位置更新丁鹉,并在應(yīng)用程序位于后臺(tái)時(shí)切換到粗粒度更新妒潭。LiveData悴能,一個(gè)生命周期感知組件,允許您的應(yīng)用在用戶更改位置時(shí)自動(dòng)更新UI杜耙。
  • 停止并開始視頻緩沖搜骡。使用生命周期感知組件盡快啟動(dòng)視頻緩沖,但推遲播放直到應(yīng)用程序完全啟動(dòng)佑女。您還可以使用生命周期感知組件在銷毀應(yīng)用程序時(shí)終止緩沖记靡。
  • 啟動(dòng)和停止網(wǎng)絡(luò)連接。使用生命周期感知組件在應(yīng)用程序處于前臺(tái)時(shí)啟用網(wǎng)絡(luò)數(shù)據(jù)的實(shí)時(shí)更新(流式傳輸)团驱,并在應(yīng)用程序進(jìn)入后臺(tái)時(shí)自動(dòng)暫停摸吠。
  • 暫停和恢復(fù)動(dòng)畫drawables。當(dāng)應(yīng)用程序在后臺(tái)時(shí)嚎花,使用生命周期感知組件處理暫停動(dòng)畫可繪制的內(nèi)容寸痢,并在應(yīng)用程序位于前臺(tái)后恢復(fù)可繪制內(nèi)容。

處理停止事件

當(dāng)一個(gè)Lifecycle屬于一個(gè)AppCompatActivityFragment紊选,所述Lifecycle的狀態(tài)改變?yōu)?CREATED與所述ON_STOP 事件被調(diào)度時(shí)AppCompatActivityFragmentonSaveInstanceState() 被調(diào) 用啼止。

當(dāng)一個(gè)Fragment或者AppCompatActivity的狀態(tài)被保存時(shí) onSaveInstanceState(),它的UI在ON_START 被調(diào)用之前被認(rèn)為是不可變的 兵罢。保存狀態(tài)后嘗試修改UI可能會(huì)導(dǎo)致應(yīng)用程序的導(dǎo)航狀態(tài)不一致献烦,這就是為什么FragmentManager 在應(yīng)用程序運(yùn)行FragmentTransaction 保存后狀態(tài)時(shí)會(huì)拋出異常的 原因。詳情 commit()請(qǐng)見卖词。

LiveData如果觀察者的關(guān)聯(lián)Lifecycle 不是至少 巩那,則通過避免調(diào)用其觀察者來防止這種邊緣情況開箱即用STARTED。在幕后此蜈,它 isAtLeast() 在決定調(diào)用其觀察者之前調(diào)用即横。

不幸的是,AppCompatActivityonStop() 方法被調(diào)用后 onSaveInstanceState()裆赵,留下其中UI狀態(tài)东囚,但不允許更改,但差距 Lifecycle 還沒有移動(dòng)到 CREATED 狀態(tài)战授。

為防止出現(xiàn)此問題舔庶,Lifecycle 版本beta2 和較低的類將狀態(tài)標(biāo)記為 CREATED 不調(diào)度事件,以便檢查當(dāng)前狀態(tài)的任何代碼都獲得實(shí)際值陈醒,即使事件未被調(diào)度惕橙,直到onStop() 系統(tǒng)調(diào)用。

不幸的是钉跷,這個(gè)解決方案有兩個(gè)主要問題

  • 在API級(jí)別23和更低級(jí)別弥鹦,Android系統(tǒng)實(shí)際上保存了活動(dòng)的狀態(tài),即使它被另一個(gè)活動(dòng)部分覆蓋。換句話說彬坏,Android系統(tǒng)調(diào)用onSaveInstanceState() 但不一定要調(diào)用onStop()朦促。這會(huì)創(chuàng)建一個(gè)潛在的長(zhǎng)間隔,即使無法修改其UI狀態(tài)栓始,觀察者仍然認(rèn)為生命周期處于活動(dòng)狀態(tài)务冕。
  • 任何想要向類公開類似行為的 LiveData類都必須實(shí)現(xiàn)Lifecycleversion beta 2和lower 提供的解決方法 。

注意: 為了使此流更簡(jiǎn)單并提供與舊版本的更好兼容性幻赚,從版本開始1.0.0-rc1禀忆,Lifecycle 對(duì)象被標(biāo)記為CREATEDON_STOPonSaveInstanceState() 調(diào)用時(shí)調(diào)度,而不等待對(duì)onStop() 方法的調(diào)用落恼。這不太可能影響您的代碼箩退,但您需要注意這一點(diǎn),因?yàn)樗cActivityAPI級(jí)別26及更低級(jí)別的類中的調(diào)用順序不匹配佳谦。

其他資源
Lifecyle感知組件是Android Jetpack 的一部分戴涝。在Sunflower演示應(yīng)用程序中查看它們的使用情況。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钻蔑,一起剝皮案震驚了整個(gè)濱河市啥刻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咪笑,老刑警劉巖可帽,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蒲肋,居然都是意外死亡蘑拯,警方通過查閱死者的電腦和手機(jī)钝满,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門兜粘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弯蚜,你說我怎么就攤上這事孔轴。” “怎么了碎捺?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵路鹰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我收厨,道長(zhǎng)晋柱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任诵叁,我火速辦了婚禮雁竞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己碑诉,他們只是感情好彪腔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著进栽,像睡著了一般德挣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上快毛,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天格嗅,我揣著相機(jī)與錄音,去河邊找鬼祸泪。 笑死吗浩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的没隘。 我是一名探鬼主播懂扼,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼右蒲!你這毒婦竟也來了阀湿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤瑰妄,失蹤者是張志新(化名)和其女友劉穎陷嘴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體间坐,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灾挨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了竹宋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劳澄。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蜈七,靈堂內(nèi)的尸體忽然破棺而出秒拔,到底是詐尸還是另有隱情,我是刑警寧澤飒硅,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布砂缩,位于F島的核電站,受9級(jí)特大地震影響三娩,放射性物質(zhì)發(fā)生泄漏庵芭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一雀监、第九天 我趴在偏房一處隱蔽的房頂上張望双吆。 院中可真熱鬧,春花似錦、人聲如沸伊诵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曹宴。三九已至搂橙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間笛坦,已是汗流浹背区转。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留版扩,地道東北人废离。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像礁芦,于是被迫代替她去往敵國和親蜻韭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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