概述
- LiveData是一個可觀察的數據持有者類。
- 與常規(guī)observable不同,LiveData是生命周期感知的,這意味著它尊重其他應用程序組件的生命周期,例如活動,片段或服務。
此感知確保LiveData僅更新處于活動的生命周期狀態(tài)的應用程序組件觀察者。
如果Observer類表示的觀察者生命周期處于STARTED或RESUMED狀態(tài),LiveData會認為它處于活動狀態(tài)。
LiveData僅通知活動觀察者有關更新的信息。 注冊觀看LiveData對象的非活動觀察者不會收到有關更改的通知。
您可以注冊與實現LifecycleOwner接口的對象配對的觀察者。 此關系允許在相應Lifecycle對象的狀態(tài)更改為DESTROYED時刪除觀察者。 這對于Activity和Fragment特別有用,因為它們可以安全地觀察LiveData對象而不用擔心泄漏 - Activity和Fragment在其生命周期被破壞時立即取消訂閱。
一、使用LiveData的優(yōu)點
確保UI符合數據狀態(tài)
LiveData遵循觀察者模式。 生命周期狀態(tài)更改時,LiveData會通知Observer對象。 您可以合并代碼以更新這些Observer對象中的UI。
沒有內存泄漏
觀察者綁定到Lifecycle對象并在其相關生命周期被破壞后自行清理。
由于停止活動而沒有崩潰
如果觀察者的生命周期處于非活動狀態(tài)(例如,在后端堆棧中的活動的情況下),則它不會接收任何LiveData事件。
不再需要手動生命周期處理
UI組件只是觀察相關數據,不會停止或恢復觀察。 LiveData自動管理所有這些,因為它在觀察時意識到相關的生命周期狀態(tài)變化。
始終保持最新數據
如果生命周期變?yōu)榉腔顒訝顟B(tài),則會在再次變?yōu)榛顒訝顟B(tài)時接收最新數據。 例如,后臺活動在返回前臺后立即收到最新數據。
適當的配置更改
如果由于配置更改(例如設備輪換)而重新創(chuàng)建Activity或Fragment,則會立即接收最新的可用數據。
共享資源
您可以使用單例模式擴展LiveData對象以包裝系統(tǒng)服務,以便可以在應用程序中共享它們。 LiveData對象連接到系統(tǒng)服務一次,然后任何需要該資源的觀察者都可以只觀看LiveData對象。
二、使用LiveData對象
請按照以下步驟使用LiveData對象:
- 創(chuàng)建LiveData實例以保存特定類型的數據。 這通常在您的ViewModel類中完成。
- 創(chuàng)建一個Observer對象,該對象定義
onChanged()方法,該方法控制LiveData對象保持數據更改時發(fā)生的情況。 您通常在UI控制器中創(chuàng)建一個Observer對象,例如Activity或Fragment。 - 使用
observe()方法將Observer對象附加到LiveData對象。observe()方法采用LifecycleOwner對象。 這會將Observer對象訂閱到LiveData對象,以便通知它更改。 您通常將Observer對象附加到UI控制器中,例如Activity或Fragment。
注意:您可以使用
observeForever(Observer)方法注冊沒有關聯的LifecycleOwner對象的觀察者。 在這種情況下,觀察者被認為始終處于活動狀態(tài),因此始終會收到有關修改的通知。 您可以刪除這些調用removeObserver(Observer)方法的觀察者。
更新存儲在LiveData對象中的值時,只要附加的LifecycleOwner處于活動狀態(tài),它就會觸發(fā)所有已注冊的觀察者。
LiveData允許UI控制器觀察者訂閱更新。 當LiveData對象保存的數據發(fā)生更改時,UI會自動更新響應。
創(chuàng)建LiveData對象
- LiveData是一個包裝器,可以與任何數據一起使用,包括實現集合的對象,例如List。
- LiveData對象通常存儲在ViewModel對象中,并通過getter方法訪問,如以下示例所示:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData<String> mCurrentName;
public MutableLiveData<String> getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData<String>();
}
return mCurrentName;
}
// Rest of the ViewModel...
}
最初,未設置LiveData對象中的數據。
注意:確保存儲更新ViewModel對象中的UI的LiveData對象,而不是活動或片段,原因如下:
- 避免臃腫的Activityb和Fragment。 現在,這些UI控制器負責顯示數據但不保持數據狀態(tài)。
- 將LiveData實例與特定Activity或Fragment實例分離,并允許LiveData對象在配置更改后繼續(xù)存在。
觀察LiveData對象
在大多數情況下,app組件的onCreate()方法是開始觀察LiveData對象的正確位置,原因如下:
- 確保系統(tǒng)不會從Activity和Fragment的
onResume()方法進行冗余調用。 - 確?;顒踊蚱尉哂锌稍谄渥?yōu)榛顒訝顟B(tài)時立即顯示的數據。 一旦應用程序組件處于
STARTED狀態(tài),它就會從它正在觀察的LiveData對象中接收最新值。 只有在設置了要觀察的LiveData對象時才會出現這種情況。
通常,LiveData僅在數據更改時才傳遞更新,并且僅在活動觀察者時傳遞更新。 此行為的一個例外是觀察者在從非活動狀態(tài)更改為活動狀態(tài)時也會收到更新。 此外,如果觀察者第二次從非活動狀態(tài)更改為活動狀態(tài),則只有在自上次活動狀態(tài)以來該值發(fā)生更改時才會收到更新。
以下示例代碼說明了如何開始觀察LiveData對象:
public class NameActivity extends AppCompatActivity {
private NameViewModel mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
mModel = ViewModelProviders.of(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
mNameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
mModel.getCurrentName().observe(this, nameObserver);
}
}
在使用nameObserver作為參數傳遞調用observe()之后,立即調用onChanged(),提供存儲在mCurrentName中的最新值。 如果LiveData對象未在mCurrentName中設置值,則不會調用onChanged()。
更新LiveData對象
- LiveData沒有公開的方法來更新存儲的數據。
- MutableLiveData類公開
setValue(T)和postValue(T)方法,如果需要編輯存儲在LiveData對象中的值,則必須使用這些方法。 - 通常在ViewModel中使用MutableLiveData,然后ViewModel僅向觀察者公開不可變的LiveData對象。
- 設置觀察者關系后,可以更新LiveData對象的值,如以下示例所示,當用戶點擊按鈕時觸發(fā)所有觀察者:
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
mModel.getCurrentName().setValue(anotherName);
}
});
在示例中調用setValue(T)會導致觀察者使用值"John Doe"調用其onChanged()方法。 該示例顯示按下按鈕,但可以調用setValue()或postValue()以更新mName,原因有多種,包括響應網絡請求或數據庫負載完成; 在所有情況下,對setValue()或postValue()的調用都會觸發(fā)觀察者并更新UI。
注意:必須調用
setValue(T)方法才能從主線程更新LiveData對象。 如果代碼在工作線程中執(zhí)行,則可以使用postValue(T)方法來更新LiveData對象。
與Room一起使用LiveData
Room持久性庫支持可觀察的查詢,這些查詢返回LiveData對象。 可觀察查詢作為數據庫訪問對象(DAO)的一部分編寫。
在更新數據庫時,Room會生成更新LiveData對象所需的所有代碼。 生成的代碼在需要時在后臺線程上異步運行查詢。 此模式對于使UI中顯示的數據與存儲在數據庫中的數據保持同步非常有用。
三、擴展LiveData
如果觀察者的生命周期處于STARTED或RESUMED狀態(tài),LiveData會將觀察者視為處于活動狀態(tài)。
以下示例代碼說明了如何擴展LiveData類:
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager mStockManager;
private SimplePriceListener mListener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
public StockLiveData(String symbol) {
mStockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
mStockManager.requestPriceUpdates(mListener);
}
@Override
protected void onInactive() {
mStockManager.removeUpdates(mListener);
}
}
此示例中價格監(jiān)聽器的實現包括以下重要方法:
- 當LiveData對象具有活動觀察者時,將調用
onActive()方法。 這意味著您需要從此方法開始觀察股票價格更新。 - 當LiveData對象沒有任何活動觀察者時,將調用
onInactive()方法。 由于沒有觀察者正在收聽,因此沒有理由保持與StockManager服務的連接。 -
setValue(T)方法更新LiveData實例的值,并通知任何活動觀察者有關更改的信息。
您可以使用StockLiveData類,如下所示:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LiveData<BigDecimal> myPriceListener = ...;
myPriceListener.observe(this, price -> {
// Update the UI.
});
}
}
observe()方法將片段(LifecycleOwner的一個實例)作為第一個參數傳遞。 這樣做表示此觀察者綁定到與所有者關聯的Lifecycle對象,這意味著:
- 如果Lifecycle對象未處于活動狀態(tài),則即使值發(fā)生更改,也不會調用觀察者。
- 銷毀Lifecycle對象后,會自動刪除觀察者。
LiveData對象具有生命周期感知這一事實意味著您可以在多個活動,片段和服務之間共享它們。
為了簡化示例,您可以將LiveData類實現為單例,如下所示:
public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager mStockManager;
private SimplePriceListener mListener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
private StockLiveData(String symbol) {
mStockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
mStockManager.requestPriceUpdates(mListener);
}
@Override
protected void onInactive() {
mStockManager.removeUpdates(mListener);
}
}
您可以在片段中使用它,如下所示:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(symbol).observe(this, price -> {
// Update the UI.
});
}
}
多個片段和活動可以觀察MyPriceListener實例。 LiveData僅在系統(tǒng)服務中的一個或多個可見且處于活動狀態(tài)時才連接到系統(tǒng)服務。
四、轉換LiveData
您可能希望在將其分配給觀察者之前更改存儲在LiveData對象中的值,或者您可能需要根據另一個實例返回另一個LiveData實例的值。 Lifecycle包提供Transformations類,其中包括支持這些方案的幫助器方法。
Transformations.map()
對存儲在LiveData對象中的值應用函數,并將結果傳播到下游。
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
與map()類似,將函數應用于存儲在LiveData對象中的值,并將結果解包并調度到下游。 傳遞給switchMap()的函數必須返回一個LiveData對象,如以下示例所示:
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
您可以使用轉換方法在觀察者的生命周期中傳遞信息。 除非觀察者正在觀察返回的LiveData對象,否則不會計算轉換。 因為轉換是懶惰地計算的,所以與生命周期相關的行為被隱式傳遞下去,而不需要額外的顯式調用或依賴。
如果您認為在ViewModel對象中需要Lifecycle對象,則轉換可能是更好的解決方案。 例如,假設您有一個接受地址的UI組件并返回該地址的郵政編碼。 您可以為此組件實現樸素的ViewModel,如以下示例代碼所示:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository;
}
private LiveData<String> getPostalCode(String address) {
// DON'T DO THIS
return repository.getPostCode(address);
}
}
然后,UI組件需要從先前的LiveData對象取消注冊,并在每次調用
getPostalCode()時注冊到新實例。 此外,如果重新創(chuàng)建UI組件,它將觸發(fā)另一個對repository.getPostCode()方法的調用,而不是使用前一個調用的結果。
相反,您可以將郵政編碼查找實現為地址輸入的轉換,如以下示例所示:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveData<String> addressInput = new MutableLiveData();
public final LiveData<String> postalCode =
Transformations.switchMap(addressInput, (address) -> {
return repository.getPostCode(address);
});
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository
}
private void setInput(String address) {
addressInput.setValue(address);
}
}
在這種情況下,postalCode字段是public和final,因為該字段永遠不會更改。 postalCode字段被定義為addressInput的轉換,這意味著當addressInput更改時將調用repository.getPostCode()方法。 如果存在活動的觀察者,則如果在調用repository.getPostCode()時沒有活動的觀察者,則在添加觀察者之前不進行任何計算。
此機制允許較低級別的應用程序創(chuàng)建按需延遲計算的LiveData對象。 ViewModel對象可以輕松獲取對LiveData對象的引用,然后在它們之上定義轉換規(guī)則。
創(chuàng)建新的轉換
在您的應用中有十幾種不同的特定轉換可能很有用,但默認情況下不提供它們。 要實現自己的轉換,可以使用MediatorLiveData類,該類偵聽其他LiveData對象并處理它們發(fā)出的事件。 MediatorLiveData正確地將其狀態(tài)傳播到源LiveData對象。
五、合并多個LiveData源
MediatorLiveData是LiveData的子類,允許您合并多個LiveData源。 只要任何原始LiveData源對象發(fā)生更改,就會觸發(fā)MediatorLiveData對象的觀察者。
例如,如果UI中有可以從本地數據庫或網絡更新的LiveData對象,則可以將以下源添加到MediatorLiveData對象:
- 與存儲在數據庫中的數據關聯的LiveData對象。
- 與從網絡訪問的數據關聯的LiveData對象。
您的活動只需要觀察MediatorLiveData對象以從兩個源接收更新。