本篇文章向您介紹了用於構建Android應用程序的如下生命週期感知架構組件:html
在此代碼框中,您能夠實現上述每一個組件的示例。首先是示例應用程序,而後經過一系列步驟添加代碼,在進度時集成各類體系結構組件。java
下載代碼android
git clone git@github.com:googlecodelabs / android-lifecycles.git
複製代碼
運行程序:git
旋轉屏幕,注意計時器重置github
若是咱們按照以前的邏輯須要保存狀態,而後恢復狀態,並恢復計時器。如今您能夠用ViewModel,由於由於此類的實例在配置更改後仍然存在,例如屏幕旋轉。shell
在此步驟中,您使用ViewModel來跨屏幕旋轉保持狀態並解決您在上一步中觀察到的行爲。在上一步中,您運行了一個顯示計時器的活動。當配置更改(例如屏幕旋轉)破壞活動時,將重置此計時器。緩存
可使用ViewModel在活動或片斷的整個生命週期中保留數據。如前一步所示,活動是管理應用數據的不良選擇。活動和片斷是短暫的對象,當用戶與應用程序交互時,這些對象會頻繁建立和銷燬。 ViewModel還更適合管理與網絡通訊相關的任務,以及數據操做和持久性。bash
打開ChronoActivity2並檢查類如何檢索和使用ViewModel:網絡
ChronometerViewModel chronometerViewModel
= ViewModelProviders.of(this).get(ChronometerViewModel.class);
複製代碼
this指的是一個實例LifecycleOwner。ViewModel只要活動範圍存在,框架就會保持LifecycleOwner活力。ViewModel若是其全部者因配置更改(例如屏幕旋轉)而被銷燬,則不會銷燬A. 全部者的新實例從新鏈接到現有實例,ViewModel以下圖所示:架構
Activity或Fragment的範圍從建立到完成(或終止),您不得與銷燬混淆。請記住,當旋轉設備時,活動會被銷燬,但ViewModel與之關聯的任何實例都不會被銷燬。
運行應用程序(在「運行配置」下拉列表中選擇「 步驟2 」),並在執行如下任一操做時確認計時器未重置:
可是,若是您或系統退出應用程序,則計時器將重置。
系統會ViewModel在生命週期全部者的整個生命週期中保留內存中的實例,例如片斷或活動。系統不會持久ViewModel存儲長期存儲。
在此步驟中,您將使用a的自定義計時器替換前面步驟中使用的計時器Timer,並每秒更新UI。 Timer是一個java.util可用於在未來循環安排任務的類。您將此邏輯添加到LiveDataTimerViewModel類中,並使活動專一於管理用戶和UI之間的交互。
當計時器通知時,活動會更新UI。爲了不內存泄漏,ViewModel不包括對活動的引用。例如,配置更改(例如屏幕旋轉)可能會致使對ViewModel應該進行垃圾回收的活動的引用。系統將保留實例,ViewModel直到相應的活動或生命週期全部者再也不存在。
注意:存儲到一個參考上下文或視圖中ViewModel可能會致使內存泄漏。避免使用引用Context或View類實例的字段。所述onCleared()方法是用於清除引用有用退訂或明確的引用與長週期其它的目的,但不Context或View對象。
ViewModel您能夠將活動或片斷配置爲觀察數據源,而不是直接從其中修改視圖,並在數據更改時接收數據。這種安排稱爲觀察者模式。
注意:要將數據公開爲可觀察對象,請將類型包裝在LiveData類中。
若是您使用了數據綁定庫或其餘反應庫(如RxJava),您可能熟悉觀察者模式。LiveData是一個特殊的可觀察類,它是生命週期感知的,只通知活躍的觀察者。
ChronoActivity3是一個實例LifecycleActivity,它能夠提供生命週期的狀態。這是類聲明:
public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {...}
複製代碼
將LifecycleRegistryOwner用於的實例的生命週期結合ViewModel並LiveData與活動或片斷。片斷的等價類是LifecycleFragment。
1.將如下代碼添加到方法中的ChronoActivity3類中subscribe()以建立訂閱:
mLiveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver);
複製代碼
2.接下來,在LiveDataTimerViewModel類中設置新的通過時間值。找到如下注釋
//TODO set the new value
複製代碼
用如下語句替換註釋:
mElapsedTime.postValue(newValue);
複製代碼
3.運行應用程序並在Android Studio中打開Android Monitor。請注意,除非您導航到另外一個應用程序,不然日誌會每秒更新。若是您的設備支持多窗口模式,您可能想嘗試使用它。旋轉屏幕不會影響應用的行爲方式。
注意:LiveData對象僅在活動時發送更新,或者處於LifecycleOwner活動狀態。若是您導航到其餘應用程序,日誌消息會暫停,直到您返回。LiveData對象僅在其各自的生命週期全部者爲STARTED或時將訂閱視爲活動的RESUMED。
許多Android組件和庫要求您:
未能完成上述步驟可能會致使內存泄漏和細微錯誤。
能夠將生命週期全部者對象傳遞給生命週期感知組件的新實例,以確保它們瞭解生命週期的當前狀態。
您可使用如下語句查詢生命週期的當前狀態:
lifecycleOwner.getLifecycle().getCurrentState()
複製代碼
上面的語句返回一個狀態,例如Lifecycle.State.RESUMED,或Lifecycle.State.DESTROYED。
實現的生命週期感知對象LifecycleObserver還能夠觀察生命週期全部者狀態的變化:
lifecycleOwner.getLifecycle().addObserver(this);
複製代碼
您能夠註釋對象以指示它在須要時調用適當的方法:
@OnLifecycleEvent(Lifecycle.EVENT.ON_RESUME)
void addLocationListener() { ... }
複製代碼
在此步驟中,您將建立一個對活動生命週期全部者做出反應的組件。使用片斷做爲生命週期全部者時,相似的原則和步驟適用。
您使用Android框架LocationManager獲取當前的緯度和經度並將其顯示給用戶。此添加容許您:
您一般會訂閱活動或方法中的LocationManager更改,並刪除或方法中的偵聽器:onStart() onResume() onStop() onPause()
// Typical use, within an activity.
@Override
protected void onResume() {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mListener);
}
@Override
protected void onPause() {
mLocationManager.removeUpdates(mListener);
}
複製代碼
在此步驟中,您將在LifecycleOwner名爲LifecycleRegistryOwner的類中使用被調用的實現BoundLocationManager。BoundLocationManager類的名稱指的是類的實例綁定到活動的生命週期。
要讓類觀察活動的生命週期,必須將其添加爲觀察者。要實現此BoundLocationManager目的,請經過將如下代碼添加到其構造函數來指示對象觀察生命週期:
lifecycleOwner.getLifecycle().addObserver(this);
複製代碼
要在發生生命週期更改時調用方法,可使用@OnLifecycleEvent註釋。使用類中的如下注釋更新addLocationListener()和removeLocationListener()方法BoundLocationListener:
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void addLocationListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void removeLocationListener() {
...
}
複製代碼
注意:觀察者被帶到提供者的當前狀態,所以不須要addLocationListener()從構造函數調用。當觀察者被添加到生命週期全部者時,它會被調用。
旋轉設備時,運行應用程序並驗證日誌監視器是否顯示如下操做:
D / BoundLocationMgr:添加了監聽器
D / BoundLocationMgr:已刪除偵聽器
D / BoundLocationMgr:添加了監聽器
D / BoundLocationMgr:已刪除偵聽器
複製代碼
使用Android模擬器模擬更改設備的位置(單擊三個點以顯示擴展控件)。在TextView當它改變時被更新:
使用ViewModel完成如下附加步驟在Fragment之間的通訊:
運行此步驟並注意其中兩個實例SeekBar彼此獨立:
使用ViewModel以便在SeekBar更改Fragment時SeekBar更新另外一個Fragment:
注意:您應該將活動用做生命週期全部者,由於每一個Fragment的生命週期是獨立的。
本節中使用的模塊處於alpha階段。這意味着API不是最終的,未來可能會發生變化。
從內存管理概述:
當用戶在應用程序之間切換時,Android會保留非前景的應用程序 - 即用戶不可見或在最近最少使用(LRU)緩存中運行音樂播放等前臺服務。例如,當用戶首次啓動應用程序時,會爲其建立一個進程; 可是當用戶離開應用程序時,該進程不會退出。系統會保持進程緩存。若是用戶稍後返回應用程序,系統將從新使用該過程,從而使應用程序切換更快。
因爲系統內存不足,它會從最近最少使用的進程開始殺死緩存中的進程。當用戶導航迴應用程序時,系統將在新進程中從新啓動應用程序。
因爲只有在用戶暫時沒有與應用程序交互時纔會發生這種狀況,所以可能容許他們返回應用程序並在初始狀態下找到它。可是,在某些狀況下,您可能但願保存應用程序的狀態或部分應用程序的狀態,以便在進程被殺死時不會丟失該信息。
該 lifecycle-viewmodel-savedstate模塊提供對ViewModel中已保存狀態的訪問。
該模塊的gradle依賴是
"androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedStateVersion"
複製代碼
ViewModels須要兩次更改才能訪問已保存的狀態:
首先,讓咱們在沒有這些變化的狀況下嘗試第6步:
1.打開運行配置「步驟6」
你會看到一個簡單的表單:
2.更更名稱,而後單擊「保存」。這將把它存儲在ViewModel內的LiveData中。
3.模擬系統終止進程(須要運行P +的仿真器)。首先輸入如下命令確保進程正在運行:
$ adb shell ps -A | grep lifecycle
複製代碼
這應該輸出帶有名稱的運行進程 com.example.android.codelabs.lifecycle
在設備或模擬器上按Home,而後運行
$ adb shell am kill com.example.android.codelabs.lifecycle
複製代碼
若是你再輸入一次
$ adb shell ps -A | grep lifecycle
複製代碼
你應該什麼也得不到,代表這個過程已被正確殺死。
4.再次打開應用程序(在應用程序啓動器中查找LC Step6)。
ViewModel中的值未保留,但已EditText恢復其狀態。這怎麼可能?
一些UI元素,包括EditText使用本身的onSaveInstanceState實現保存其狀態。在進程被終止後,此狀態將在配置更改後恢復時恢復。閱讀ViewModels:Persistence,onSaveInstanceState(),恢復UI狀態和加載器以獲取更多信息。
實際上,該lifecycle-viewmodel-savedstate模塊還使用onSaveInstanceState和onRestoreInstanceState保持ViewModel狀態,但它使這些操做更方便。
爲ViewModel實現保存的狀態 在SavedStateActivity.java文件中,替換
mSavedStateViewModel = ViewModelProviders.of(this).get(SavedStateViewModel.class);
複製代碼
爲:
mSavedStateViewModel = ViewModelProviders.of(this, new SavedStateVMFactory(this))
.get(SavedStateViewModel.class);
複製代碼
在SavedStateViewModel.java文件中,您須要添加一個新的構造函數,該構造函數SavedStateHandle將狀態存儲在私有字段中:
private SavedStateHandle mState;
public SavedStateViewModel(SavedStateHandle savedStateHandle){
mState = savedStateHandle;
}
複製代碼
如今您將使用該模塊的LiveData支持,所以您再也不須要存儲它:
private static final String NAME_KEY =「name」;
//公開不可變的LiveData
LiveData <String> getName(){
return mState.getLiveData(NAME_KEY);
}
void saveNewName(String newName){
mState.set(NAME_KEY,newName);
}
複製代碼
如今您正在使用LiveData mState,MutableLiveData名稱再也不使用,能夠刪除。
如今您能夠再次嘗試相同的過程。打開應用程序,更更名稱並保存。而後,按Home並使用如下命令終止該過程:
$ adb shell am kill com.example.android.codelabs.lifecycle
複製代碼
若是您從新打開該應用程序,您將看到ViewModel中的狀態此時已保存。