* 使用 LiveData 的優點
* 使用 LiveData 對象
* 創建 LiveData 對象
* 觀察 LiveData 對象
* 更新 LiveData 對象
* LiveData 與 Room 一起使用
* 擴展 LiveData
* 轉換 LiveData
* 創建新的轉換
* 合并多個 LiveData 源
* 其他資源
LiveData 是一個可觀察的數據持有者類。與常規 observable 不同,LiveData 是生命周期感知的,這意味著它尊重其他應用程序組件的生命周期,例如 activity,fragment 或 service。此感知確保 LiveData 僅更新處于活動狀態的應用組件觀察者。
注意:要將 LiveData 組件導入 Android 項目,請參閱 Adding Components to your Project。
如果 Observer 類表示的觀察者生命周期處于 STARTED
或 RESUMED
狀態,則 LiveData 會將其視為活動狀態。LiveData 僅通知處于活動狀態的觀察者更新信息。非活動狀態的觀察者不會收到有關數據更改的通知。
你可以注冊與實現了 LifecycleOwner
接口的對象配對的觀察者。此關系允許在相應 Lifecycle
對象的狀態更改為 DESTROYED
時刪除觀察者。這對于 activity 和 fragment 特別有用,因為它們可以安全地觀察 LiveData 對象而不用擔心泄漏 - activity 和 fragment 在其生命周期被銷毀時立即取消訂閱。
一、使用 LiveData 的優點
使用 LiveData 具有以下優勢:
確保你的 UI 符合你的數據狀態
LiveData 遵循觀察者模式。生命周期狀態更改時,LiveData 會通知 Observer 對象。你的觀察者可以在每次數據更改時更新 UI。
沒有內存泄漏
觀察者綁定到 Lifecycle 對象,并在其相關生命周期被銷毀后自行清理。
不會因為 activity 停止而發生崩潰
如果觀察者的生命周期處于非活動狀態(例如,activity 在后臺堆棧中),則它不會接收任何 LiveData 事件。
不再需要手動處理生命周期
UI 組件只是觀察相關數據,不會停止或恢復觀察。LiveData 自動管理所有這些,因為它在觀察時意識到相關的生命周期狀態變化。
始終保持最新數據
如果生命周期變為非活動狀態,它將在再次變為活動狀態時接收最新數據。例如,后臺 activity 在返回前臺后立即接收最新數據。
適當的配置更改
如果由于配置更改(例如設備旋轉)而重新創建 activity 或 fragment,則會立即接收最新的可用數據。
共享資源
你可以使用單例模式擴展 LiveData 對象以包裝系統服務,以便可以在應用程序中共享它們。LiveData 對象連接到系統服務一次,然后任何需要該資源的觀察者只需觀察 LiveData 對象。
二、使用 LiveData 對象
請按照以下步驟使用 LiveData 對象:
創建 LiveData 實例以保存特定類型的數據。這通常在 ViewModel 類中完成。
創建一個 Observer 對象,該對象定義
onChanged()
方法,該方法控制 LiveData 對象數據更改時觸發的邏輯。通常在 UI 控制器中創建一個 Observer 對象,例如 activity 或 fragment。使用
observe()
方法將 Observer 對象關聯到 LiveData 對象。observe()
方法需要LifecycleOwner
對象。該方法使 Observer 對象訂閱 LiveData 對象,以便通知它更改。通常將 Observer 對象關聯到 UI 控制器中,例如 activity 或 fragment。
注意:你可以使用
observeForever(Observer)
方法注冊沒有關聯任何一個LifecycleOwner
對象的觀察者。在這種情況下,觀察者被認為始終處于活動狀態,因此始終會收到有關修改的通知。你可以刪除這些觀察者通過調用removeObserver(Observer)
方法。
更新存儲在 LiveData 對象中的值時,只要關聯的 LifecycleOwner
處于活動狀態,它就會觸發所有已注冊的觀察者。
LiveData 允許 UI 控制器觀察者訂閱更新。當 LiveData 對象保存的數據發生更改時,UI 會自動響應更新。
2.1 創建 LiveData 對象
LiveData 是一個包裝器,可以與任何數據一起使用,包括實現集合的對象,例如 List。LiveData
對象通常存儲在 ViewModel
對象中,并通過 getter
方法訪問,如以下示例所示:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData<String> currentName;
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
// Rest of the ViewModel...
}
最初的時候并沒有設置 LiveData 對象中的數據。
注意:確保在 ViewModel 對象中存儲用于更新 UI 的 LiveData 對象,而不是在 activity 或 fragment 中,原因如下:
避免臃腫的 activity 和 fragment。UI 控制器負責顯示數據但不保持數據狀態。
將 LiveData 實例與特定 activity 或 fragment 實例分離,并允許 LiveData 對象在配置更改后繼續存活。
你可以在 ViewModel 指南中閱讀有關 ViewModel 類的優點和用法的更多信息。
2.2 觀察 LiveData 對象
在大多數情況下,app 組件的 onCreate()
方法是開始觀察 LiveData 對象的正確位置,原因如下:
確保系統不會從 activity 或 fragment 的
onResume()
方法進行冗余調用。確保 activity 或 fragment 在其變為活動狀態時立即顯示數據。只要應用程序組件處于
STARTED
狀態,它就會從它正在觀察的 LiveData 對象中接收最新值。只有在設置了要觀察的 LiveData 對象時才會出現這種情況。
通常,LiveData 僅在數據更改時才提供更新,并且要求觀察者處于活動狀態。此行為的一個例外是觀察者從非活動狀態更改為活動狀態時也會收到更新。此外,如果觀察者第二次從非活動狀態更改為活動狀態,則只有在自上次活動狀態以來該值發生更改時才會收到更新。
以下示例代碼說明了如何開始觀察 LiveData 對象:
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
model = 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.
nameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}
在使用 nameObserver
作為參數傳遞調用 observe()
之后,立即調用 onChanged()
,提供存儲在 mCurrentName
中的最新值。如果 LiveData
對象未在 mCurrentName
中設置值,則不會調用 onChanged()
。
2.3 更新 LiveData 對象
LiveData 沒有公開的方法來更新存儲的數據。MutableLiveData
類公開 setValue(T)
和 postValue(T)
方法,如果需要編輯存儲在 LiveData 對象中的值,則必須使用這些方法。通常在 ViewModel 中使用 MutableLiveData
,然后 ViewModel 僅向觀察者公開不可變的 LiveData 對象。
設置觀察者關系后,可以更新 LiveData 對象的值,如以下示例所示,當用戶點擊按鈕時觸發所有觀察者:
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
model.getCurrentName().setValue(anotherName);
}
});
在示例中調用 setValue(T)
會導致觀察者使用值 John Doe 調用其 onChanged()
方法。該示例顯示按下按鈕,調用 setValue()
或 postValue()
來更新 mName,更新的原因可以有多種,包括響應網絡請求或數據庫加載完成;在所有情況下,對 setValue()
或 postValue()
的調用都會觸發觀察者并更新 UI。
注意:必須調用
setValue(T)
方法才能從主線程更新 LiveData 對象。如果代碼在工作線程中執行,則可以使用postValue(T)
方法來更新 LiveData 對象。
2.4 LiveData 與 Room 一起使用
Room 持久性庫支持可觀察的查詢,這些查詢返回 LiveData
對象。可觀察查詢作為數據庫訪問對象 (DAO) 的一部分寫入。
在更新數據庫時,Room 會生成更新 LiveData 對象所需的所有代碼。生成的代碼在需要時在后臺線程上異步運行查詢。此模式對于使 UI 中顯示的數據與存儲在數據庫中的數據保持同步非常有用。你可以在 Room 持久性庫指南中閱讀有關 Room 和 DAO 的更多信息。
三、擴展 LiveData
如果觀察者的生命周期處于 STARTED
或 RESUMED
狀態,LiveData 會將觀察者視為處于活動狀態。以下示例代碼說明了如何擴展 LiveData 類:
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
public StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
此示例中價格監聽器的實現包括以下重要方法:
當
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()
方法將 fragment(LifecycleOwner
的一個實例)作為第一個參數傳遞。這樣做表示此觀察者綁定到與所有者關聯的 Lifecycle
對象,這意味著:
如果
Lifecycle
對象未處于活動狀態,則即使值發生更改,也不會調用觀察者。銷毀
Lifecycle
對象后,會自動刪除觀察者。
LiveData 對象具有生命周期感知這一事實意味著你可以在多個 activity,fragment 和 service 之間共享它們。為了簡化示例,你可以將 LiveData 類實現為單例,如下所示:
public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager stockManager;
private SimplePriceListener listener = 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) {
stockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
你可以在 fragment 中使用它,如下所示:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(symbol).observe(this, price -> {
// Update the UI.
});
}
}
多個 fragment 和 activity 可以觀察 MyPriceListener 實例。LiveData 僅在一個或多個 fragment 和 activity 可見且處于活動狀態時才連接到系統服務。
四、轉換 LiveData
你可能希望在將其分配給觀察者之前更改存儲在 LiveData 對象中的值,或者你可能需要根據另一個實例返回另一個 LiveData 實例的值。Lifecycle 包提供 Transformations 類,其中包括支持這些場景的幫助方法。
對存儲在 LiveData
對象中的值應用函數,并往后傳遞結果。
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
與 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()
時注冊到新實例。此外,如果重新創建 UI 組件,它將觸發對 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 字段被定義為 addressInput 的轉換。只要你的應用程序具有與 postalCode 字段關聯的處于活動狀態的觀察者,只要 addressInput 更改,就會重新計算并檢索字段的值。
此機制允許較低級別的應用程序創建按需延遲計算的 LiveData
對象。ViewModel
對象可以輕松獲取對 LiveData
對象的引用,然后在它們之上定義轉換規則。
4.1 創建新的轉換
有十幾種不同的特定轉換可能對你的應用有用,但默認情況下不提供。 要實現自己的轉換,可以使用 MediatorLiveData
類,該類偵聽其他 LiveData 對象并處理它們發出的事件。MediatorLiveData
正確地將其狀態傳遞到源 LiveData 對象。 要了解有關此模式的更多信息,請參閱 Transformations 類的參考文檔。
五、合并多個 LiveData 源
MediatorLiveData
是 LiveData
的子類,允許你合并多個 LiveData
源。只要任何原始 LiveData
源對象發生更改,就會觸發 MediatorLiveData
對象的觀察者。
例如,如果你的 UI 中的 LiveData
對象,可以從本地數據庫或網絡進行更新,那么你可以添加以下來源的 MediatorLiveData
對象:
與存儲在數據庫中的數據關聯的
LiveData
對象。與從網絡訪問的數據關聯的
LiveData
對象。
你的 activity 只需要觀察 MediatorLiveData
對象以從兩個源接收更新。有關詳細示例,請參閱 Guide to App Architecture 中的 Addendum: exposing network status。
六、其他資源
LiveData
用于 Sunflower 演示應用程序。
另請參閱架構組件 BasicSample。