Jetpack學(xué)習(xí)7—生命周期感知組件

[TOC]

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

生命周期的組件執(zhí)行動作以響應(yīng)另一個組件生命周期狀態(tài)的變化,比如activities和fragments。這些組件可以幫助您生成更有組織、更輕量級、更易于維護的代碼。

一種常見模式是在activities和fragments的生命周期方法中實現(xiàn)依賴組件的操作。但是,這種模式導(dǎo)致代碼組織不良和錯誤擴散。通過使用生命周期感知組件,您可以將依賴組件的代碼移出生命周期方法并移入組件本身。

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

注意:要導(dǎo)入 android.arch.lifecycle 到Android項目中,請參閱Lifecycle發(fā)行說明中有關(guān)聲明依賴項的說明。

Android框架中定義的大多數(shù)應(yīng)用程序組件都附加了生命周期。生命周期由操作系統(tǒng)或流程中運行的框架代碼管理。它們是Android運作方式的核心,您的應(yīng)用程序必須尊重它們。不這樣做可能會觸發(fā)內(nèi)存泄漏甚至應(yī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
    }
}

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

此外,無法保證在activity或fragment停止之前啟動組件。尤其是如果我們需要執(zhí)行一個耗時操作,如在onStart()中檢測一些配置。這可能會導(dǎo)致競爭條件,onStop()方法在此之前完成onStart(),使組件保持活動的時間超過其所需的時間。這可能導(dǎo)致競爭的情況,onStop()方法在onStart()之前完成,使組件保持活動的時間超過其所需的時間。

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是一個類,它保存關(guān)于組件(如activity或fragment)的生命周期狀態(tài)的信息,并允許其他對象觀察該狀態(tài)。

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

Event

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

State

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

[站外圖片上傳中...(image-a27353-1552355707972)]

將狀態(tài)看作圖的節(jié)點,將事件看作這些節(jié)點之間的邊。

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

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對象實現(xiàn)了 LifecycleOwner 接口,將在下一節(jié)中進行說明。

LifecycleOwner

LifecycleOwner是僅有一個的方法接口,意味著該類有一個 Lifecycle。它有一個方法getLifecycle(),必須由該類實現(xiàn)。如果您試圖管理整個應(yīng)用程序流程的生命周期,請參閱ProcessLifecycleOwner。

該接口將生命周期的所有權(quán)從各個類(如FragmentAppCompatActivity)中抽象出來,并允許編寫與其工作的組件。任何自定義應(yīng)用程序類都可以實現(xiàn) LifecycleOwner 接口。

實現(xiàn)LifecycleObserver的組件與實現(xiàn)LifecycleOwner的組件可以無縫地工作,因為所有者可以提供一個生命周期,觀察者可以注冊該生命周期來觀察。

對于位置跟蹤示例,我們可以讓MyLocationListener類實現(xiàn)LifecycleObserver,然后在onCreate()方法中使用activity的Lifecycle初始化它。這使得MyLocationListener類能夠自給自足,這意味著響應(yīng)生命周期狀態(tài)變化的邏輯是在MyLocationListener中聲明的,而不是在activity中。讓各個組件存儲它們自己的邏輯可以使activities和fragments的邏輯更容易管理。

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

一個常見的用例是,如果生命周期目前處于不好的狀態(tài),則避免調(diào)用某些回調(diào)。例如,如果回調(diào)在activity狀態(tài)保存后運行fragment 事務(wù),那么它將觸發(fā)崩潰,因此我們永遠不會調(diào)用該回調(diào)。

為了簡化這個用例, Lifecycle類允許其他對象查詢當(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
    }
}

通過這種實現(xiàn),我們的LocationListener類完全可以識別生命周期。如果我們需要在另一個activity or fragment中使用LocationListener,我們只需要初始化它。所有設(shè)置和拆卸操作都由類本身管理。

如果庫提供了需要使用Android生命周期的類,我們建議您使用生命周期感知組件。您的庫客戶端可以輕松地集成這些組件,而無需在客戶端進行手動生命周期管理。

實現(xiàn)自定義LifecycleOwner

Support Library 26.1.0 和以后版本中的片段和活動已經(jīng)實現(xiàn)了LifecycleOwner接口。

如果您需要一個自定義類來實現(xiàn)LifecycleOwner,那么您可以使用LifecycleRegistry類,但是您需要將事件轉(zhuǎn)發(fā)到該類中,如下面的代碼示例所示:

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

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

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

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

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

生命周期感知組件的最佳實踐

  • 保持UI控制器(活動和片段)盡可能精簡。他們不應(yīng)該試圖獲取自己的數(shù)據(jù); 相反,使用ViewModel來執(zhí)行此操作,并觀察LiveData 對象以將更改反映回視圖。
  • 嘗試編寫數(shù)據(jù)驅(qū)動的UI,您的UI控制器負責(zé)在數(shù)據(jù)更改時更新視圖,或者將用戶操作通知給ViewModel。
  • 將數(shù)據(jù)邏輯放入ViewModel類中。ViewModel應(yīng)該作為UI控制器和應(yīng)用程序其余部分之間的連接器。不過要小心,獲取數(shù)據(jù)(例如從網(wǎng)絡(luò))不是ViewModel的職責(zé)。相反,ViewModel應(yīng)該調(diào)用適當(dāng)?shù)慕M件來獲取數(shù)據(jù),然后將結(jié)果返回給UI控制器。
  • 使用Data Binding來來維護視圖和UI控制器之間簡潔的接口。這使您可以使視圖更具說明性,并最大限度地減少在activitiesfragments中編寫所需的更新代碼。如果您喜歡在Java編程語言中這樣做,那么可以使用像Butter Knife這樣的庫來避免樣板代碼,并具有更好的抽象。
  • 如果UI比較復(fù)雜,可以考慮創(chuàng)建presenter類來處理UI修改。這可能是一個費力的任務(wù),但它可以使您的UI組件更容易測試。
  • 避免在你的ViewModel引用 View or Activity context,如果這個ViewModelActivityd的壽命長(在 configuration changes的情況下),你的activity會泄漏并且不會被垃圾回收器正確的處理

生命周期感知組件的用例

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

  • 在粗粒度和細粒度位置更新之間切換。使用生命周期感知組件可在您的位置應(yīng)用程序可見時啟用細粒度位置更新,并在應(yīng)用程序位于后臺時切換到粗粒度更新。LiveData,一個生命周期感知組件,允許您的應(yīng)用在用戶更改位置時自動更新UI。
  • 停止和啟動視頻緩沖。使用生命周期感知組件盡快啟動視頻緩沖,但推遲播放直到應(yīng)用程序完全啟動。您還可以使用生命周期感知組件在銷毀應(yīng)用程序時終止緩沖。
  • 啟動和停止網(wǎng)絡(luò)連接。使用生命周期感知組件在應(yīng)用程序處于前臺時啟用網(wǎng)絡(luò)數(shù)據(jù)的實時更新(流式傳輸),并在應(yīng)用程序進入后臺時自動暫停。
  • 暫停和恢復(fù)動畫繪制。當(dāng)app在后臺時使用生命周期感知組件處理暫停動畫繪制,并在app在前臺后恢復(fù)繪制。

處理停止事件

當(dāng)Lifecycle屬于AppCompatActivityFragment,當(dāng)AppCompatActivityFragmentonSaveInstanceState() 被調(diào)用時,該Lifecycle的狀態(tài)改變?yōu)?a target="_blank" rel="nofollow">CREATED并且ON_STOP 事件被分發(fā)。

當(dāng)FragmentAppCompatActivity的狀態(tài)通過onSaveInstanceState()保存時,直到調(diào)用ON_START之前,它的UI被認為是不可變的。保存狀態(tài)后試圖修改UI很可能會導(dǎo)致應(yīng)用程序?qū)Ш綘顟B(tài)的不一致,這就是為什么如果應(yīng)用程序在保存狀態(tài)后運行FragmentTransaction,FragmentManager會拋出異常。有關(guān)詳細信息,請參見commit()

LiveData通過檢測觀察者相關(guān)的生命周期至少在STARTED狀態(tài)才調(diào)用他的觀察者來避免防止這種情況出現(xiàn)。在幕后,它在決定調(diào)用其觀察者之前調(diào)用isAtLeast()

不幸的是,AppCompatActivityonStop()方法是在onSaveInstanceState()之后調(diào)用的,這留下了一個空白,即UI狀態(tài)更改是不允許的,但是Lifecycle還沒有移動到CREATED狀態(tài)。

為了防止出現(xiàn)這個問題,Lifecycle類在beta2和更低的版本在沒有事件調(diào)度的情況下將狀態(tài)標記為CREATED,以便任何檢查當(dāng)前狀態(tài)的代碼都獲得實際值,即使事件未被調(diào)用,直到onStop()才被系統(tǒng)調(diào)用。

不幸的是,這個解決方案有兩個主要問題:

  • 在API級別23或更低的級別上,Android系統(tǒng)實際上保存了一個活動的狀態(tài),即使它被另一個活動部分覆蓋。換句話說,Android系統(tǒng)調(diào)用onSaveInstanceState(),但它不一定調(diào)用onStop()。這就產(chǎn)生了一個潛在的長時間間隔,在這個時間間隔中,觀察者仍然認為生命周期是活動的,即使它的UI狀態(tài)不能被修改。
  • 任何想要向LiveData類公開類似行為的類都必須實現(xiàn)Lifecycle version beta 2或更低版本提供的解決方案。

注意:為了簡化此流并提供與舊版本更好的兼容性,從1.0.0-rc1版本開始,Lifecycle對象被標記為CREATED 并分派ON_STOP事件, 在onSaveInstanceState() 調(diào)用時調(diào)度,而不等待對onStop()方法的調(diào)用。這不太可能影響您的代碼,但您需要注意這一點,因為它與ActivityAPI級別26及更低級別的類中的調(diào)用順序不匹配。

其他資源

要了解有關(guān)使用生命周期感知組件處理生命周期的更多信息,請參閱以下其他資源。

例子

Codelabs

Blogs

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

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