2、Android Jetpack_Note_CodeLabs一Lifecycles+ViewModel+LiveData

1、簡介

本篇文章向您介紹了用於構建Android應用程序的如下生命週期感知架構組件:html

  • ViewModel - 提供了一種建立和檢索綁定到特定生命週期的對象的方法。A ViewModel一般存儲視圖數據的狀態,並與其餘組件通訊,例如數據存儲庫或處理業務邏輯的域層。要閱讀本主題的介紹性指南,請參閱ViewModel
  • LifecycleOwner / LifecycleRegistryOwner -不管是LifecycleOwner和LifecycleRegistryOwner是在實現的接口AppCompatActivity和Support Fragment類。您能夠將其餘組件訂閱到實現這些接口的全部者對象,以觀察對全部者生命週期的更改。要閱讀本主題的介紹性指南,請參閱Hanlde Lifecycles
  • LiveData - 容許您觀察應用程序的多個組件之間的數據更改,而無需在它們之間建立明確,嚴格的依賴關係路徑。LiveData尊重應用程序組件的複雜生命週期,包括活動,片斷,服務或LifecycleOwner應用程序中定義的任何內容。LiveData經過暫停對已中止LifecycleOwner對象的訂閱以及取消對LifecycleOwner已完成對象的訂閱來管理觀察者訂閱。要閱讀本主題的介紹性指南,請參閱LiveData

1.1 你要建造什麼

在此代碼框中,您能夠實現上述每一個組件的示例。首先是示例應用程序,而後經過一系列步驟添加代碼,在進度時集成各類體系結構組件。java

1.2 你須要什麼

  • Android Studio 3.3或更高版本
  • 熟悉Android的生命週期

2、環境搭建

下載代碼android

git clone git@github.com:googlecodelabs / android-lifecycles.git
複製代碼

運行程序:git

旋轉屏幕,注意計時器重置github

若是咱們按照以前的邏輯須要保存狀態,而後恢復狀態,並恢復計時器。如今您能夠用ViewModel,由於由於此類的實例在配置更改後仍然存在,例如屏幕旋轉。shell

3、添加ViewModel

在此步驟中,您使用ViewModel來跨屏幕旋轉保持狀態並解決您在上一步中觀察到的行爲。在上一步中,您運行了一個顯示計時器的活動。當配置更改(例如屏幕旋轉)破壞活動時,將重置此計時器。緩存

可使用ViewModel在活動或片斷的整個生命週期中保留數據。如前一步所示,活動是管理應用數據的不良選擇。活動和片斷是短暫的對象,當用戶與應用程序交互時,這些對象會頻繁建立和銷燬。 ViewModel還更適合管理與網絡通訊相關的任務,以及數據操做和持久性。bash

3.1 使用ViewModel保持計時器的狀態

打開ChronoActivity2並檢查類如何檢索和使用ViewModel網絡

ChronometerViewModel chronometerViewModel
        = ViewModelProviders.of(this).get(ChronometerViewModel.class);
複製代碼

this指的是一個實例LifecycleOwner。ViewModel只要活動範圍存在,框架就會保持LifecycleOwner活力。ViewModel若是其全部者因配置更改(例如屏幕旋轉)而被銷燬,則不會銷燬A. 全部者的新實例從新鏈接到現有實例,ViewModel以下圖所示:架構

Activity或Fragment的範圍從建立到完成(或終止),您不得與銷燬混淆。請記住,當旋轉設備時,活動會被銷燬,但ViewModel與之關聯的任何實例都不會被銷燬。

3.1 試一下

運行應用程序(在「運行配置」下拉列表中選擇「 步驟2 」),並在執行如下任一操做時確認計時器未重置:

  1. 旋轉屏幕。 2。 導航到另外一個應用程序,而後返回。

可是,若是您或系統退出應用程序,則計時器將重置。

系統會ViewModel在生命週期全部者的整個生命週期中保留內存中的實例,例如片斷或活動。系統不會持久ViewModel存儲長期存儲。

4、使用LiveData包裝數據

在此步驟中,您將使用a的自定義計時器替換前面步驟中使用的計時器Timer,並每秒更新UI。 Timer是一個java.util可用於在未來循環安排任務的類。您將此邏輯添加到LiveDataTimerViewModel類中,並使活動專一於管理用戶和UI之間的交互。

當計時器通知時,活動會更新UI。爲了不內存泄漏,ViewModel不包括對活動的引用。例如,配置更改(例如屏幕旋轉)可能會致使對ViewModel應該進行垃圾回收的活動的引用。系統將保留實例,ViewModel直到相應的活動或生命週期全部者再也不存在。

注意:存儲到一個參考上下文或視圖中ViewModel可能會致使內存泄漏。避免使用引用Context或View類實例的字段。所述onCleared()方法是用於清除引用有用退訂或明確的引用與長週期其它的目的,但不Context或View對象。

ViewModel您能夠將活動或片斷配置爲觀察數據源,而不是直接從其中修改視圖,並在數據更改時接收數據。這種安排稱爲觀察者模式。

注意:要將數據公開爲可觀察對象,請將類型包裝在LiveData類中。

若是您使用了數據綁定庫或其餘反應庫(如RxJava),您可能熟悉觀察者模式。LiveData是一個特殊的可觀察類,它是生命週期感知的,只通知活躍的觀察者。

4.1 LifecycleOwner

ChronoActivity3是一個實例LifecycleActivity,它能夠提供生命週期的狀態。這是類聲明:

public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {...}
複製代碼

將LifecycleRegistryOwner用於的實例的生命週期結合ViewModel並LiveData與活動或片斷。片斷的等價類是LifecycleFragment。

4.2 更新ChronoActivity

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。

5、訂閱生命週期事件

許多Android組件和庫要求您:

  1. 訂閱或初始化組件或庫。
  2. 取消訂閱或中止組件或庫。

未能完成上述步驟可能會致使內存泄漏和細微錯誤。

能夠將生命週期全部者對象傳遞給生命週期感知組件的新實例,以確保它們瞭解生命週期的當前狀態。

您可使用如下語句查詢生命週期的當前狀態:

lifecycleOwner.getLifecycle().getCurrentState()
複製代碼

上面的語句返回一個狀態,例如Lifecycle.State.RESUMED,或Lifecycle.State.DESTROYED。

實現的生命週期感知對象LifecycleObserver還能夠觀察生命週期全部者狀態的變化:

lifecycleOwner.getLifecycle().addObserver(this);
複製代碼

您能夠註釋對象以指示它在須要時調用適當的方法:

@OnLifecycleEvent(Lifecycle.EVENT.ON_RESUME)
void addLocationListener() { ... }
複製代碼

5.1 建立一個支持生命週期的組件

在此步驟中,您將建立一個對活動生命週期全部者做出反應的組件。使用片斷做爲生命週期全部者時,相似的原則和步驟適用。

您使用Android框架LocationManager獲取當前的緯度和經度並將其顯示給用戶。此添加容許您:

  • 訂閱更改並使用自動更新UI LiveData。
  • 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當它改變時被更新:

6、在Fragment之間共享ViewModel

6.1 在Fragment之間共享ViewModel

使用ViewModel完成如下附加步驟在Fragment之間的通訊:

  • 一項活動。
  • 一個片斷的兩個實例,每一個都有一個SeekBar。
  • 單個ViewModel帶LiveData字段。

運行此步驟並注意其中兩個實例SeekBar彼此獨立:

使用ViewModel以便在SeekBar更改Fragment時SeekBar更新另外一個Fragment:

注意:您應該將活動用做生命週期全部者,由於每一個Fragment的生命週期是獨立的。

7、跨進程保持ViewModel狀態(實驗版)

本節中使用的模塊處於alpha階段。這意味着API不是最終的,未來可能會發生變化。

從內存管理概述:

當用戶在應用程序之間切換時,Android會保留非前景的應用程序 - 即用戶不可見或在最近最少使用(LRU)緩存中運行音樂播放等前臺服務。例如,當用戶首次啓動應用程序時,會爲其建立一個進程; 可是當用戶離開應用程序時,該進程不會退出。系統會保持進程緩存。若是用戶稍後返回應用程序,系統將從新使用該過程,從而使應用程序切換更快。

因爲系統內存不足,它會從最近最少使用的進程開始殺死緩存中的進程。當用戶導航迴應用程序時,系統將在新進程中從新啓動應用程序。

因爲只有在用戶暫時沒有與應用程序交互時纔會發生這種狀況,所以可能容許他們返回應用程序並在初始狀態下找到它。可是,在某些狀況下,您可能但願保存應用程序的狀態或部分應用程序的狀態,以便在進程被殺死時不會丟失該信息。

該 lifecycle-viewmodel-savedstate模塊提供對ViewModel中已保存狀態的訪問。

該模塊的gradle依賴是

"androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedStateVersion"
複製代碼

ViewModels須要兩次更改才能訪問已保存的狀態:

  1. 傳遞SavedStateVMFactory到ViewModelProvider
  2. 添加一個接收a的構造函數 SavedStateHandle

首先,讓咱們在沒有這些變化的狀況下嘗試第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中的狀態此時已保存。

相關文章
相關標籤/搜索