相關(guān)文章:
- 【翻譯】安卓架構(gòu)組件(1)-App架構(gòu)指導
- 【翻譯】安卓架構(gòu)組件(2)-添加組件到你的項目中
- 【翻譯】安卓架構(gòu)組件(3)-處理生命周期
- 【翻譯】安卓架構(gòu)組件(5)-ViewModel
- 【翻譯】安卓架構(gòu)組件(6)-Room持久化類庫
- 【翻譯】安卓架構(gòu)組件(7)-分頁庫
LiveData是一個數(shù)據(jù)持有類,保存了數(shù)據(jù)值以及允許該值被觀察。并不像常規(guī)的被觀察者,LiveData遵循app組件的生命周期,例如Observer可以指定要觀察的Lifecyle。
如果Observer的Lifecycle是STARTED或者RESUMED狀態(tài),則LiveData認為其處在激活狀態(tài)。
public class LocationLiveData extends LiveData<Location> {
private LocationManager locationManager;
private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};
public LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}
在Location監(jiān)聽器的實現(xiàn)中有三個重要的地方:
-
onActive():當LiveData擁有一個處在激活狀態(tài)的觀察者時會調(diào)用這個方法。這意味著我們需要開始觀察位置信息的更新 -
onInactive():當LiveData并不擁有任何處在激活狀態(tài)時這個方法被調(diào)用。既然沒有任何觀察者進行監(jiān)聽,那么沒有理由繼續(xù)保持對LocationManager服務的連接 -
setValue():調(diào)用該方法更新LiveData實例的值,并通知處在激活狀態(tài)的觀察者該變化
我們可以這樣使用新的LocationLiveData:
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
LiveData<Location> myLocationListener = ...;
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.observer(this, location -> {
// 更新 UI
});
}
});
}
}
注意到addObserver()方法傳遞的第一個參數(shù)是LifecycleOwner,這表示觀察者必然綁定至Lifecycle,這意味著:
- 如果
Lifecycle并不處在激活狀態(tài),即使值發(fā)生變化,觀察者也不會被響應 - 如果
Lifecycle被銷毀,觀察者會被自動清除
LiveData是生命周期敏感的,這提供給了我們一個新的機會:我們可以在多個Activity、多個Fragment間共享。為了保持我們樣例代碼的簡潔,我們將它變成單例:
public class LocationLiveData extends LiveData<Location> {
private static LocationLiveData sInstance;
private LocationManager locationManager;
@MainThread
public static LocationLiveData get(Context context) {
if (sInstance == null) {
sInstance = new LocationLiveData(context.getApplicationContext());
}
return sInstance;
}
private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};
private LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}
現(xiàn)在Fragment可以這樣用:
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
Util.checkUserStatus(result -> {
if (result) {
LocationLiveData.get(getActivity()).observe(this, location -> {
// 更新 UI
});
}
});
}
}
可能會有多個Fragment和多個Activity觀察我們的MyLocationListener實例,并且只要它們處在激活狀態(tài),我們的LiveData可以優(yōu)雅地進行管理。
LiveData類具有如下的優(yōu)點:
- 沒有內(nèi)存泄漏:因為
Obeserver們被綁定在自己的Lifecycle對象,當Lifecycle被銷毀的時候,它們會被自動清除 - 停止
Activity的時候不會發(fā)生崩潰:如果Obeserver們的Lifecycle對象處在非激活狀態(tài)(如Activity在后臺),它們將不會接收任何變化事件 - 總是更新數(shù)據(jù):如果
Lifecycle再次啟動(例如一個Activity從后臺回到前臺),它將會收到最新的本地數(shù)據(jù) - 正確的配置變化:如果一個
Activity或一個Fragment由于配置的變化(如屏幕旋轉(zhuǎn))重新創(chuàng)建,它將會立刻收到最新的Location數(shù)據(jù) - 共享資源:現(xiàn)在我們可以持有一個單獨的
MyLocationListener實例,只需要連接到系統(tǒng)服務一次,并且可以正確地支持app里的所有觀察者 - 不需要手動處理生命周期:你應該注意到,我們的
Fragment僅僅當想要的時候觀察數(shù)據(jù),不需要關(guān)心如何停止,以及在停止后的再次開啟。LiveData會自動管理所有的情況,因為Fragment提供了其Lifecycle。
LiveData轉(zhuǎn)換
有些時候,你可能想要在分發(fā)LiveData至觀察者之前做一些變化,或者需要基于當前值返回另一個LiveData實例。
Lifecycle包提供了一個Transformations類,包含這些操作的輔助方法。
Transformations.map()
對LiveData值應用一個函數(shù),傳遞結(jié)果至下流。
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
和map()相似,傳遞至switchMap()的函數(shù)必須返回一個Lifecycle。
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
使用這些轉(zhuǎn)化允許通過鏈繼續(xù)觀察Lifecycle信息,例如這些信息只有當一個觀察者觀察返回LiveData的時才進行計算。這種惰性計算的特性允許在轉(zhuǎn)化過程中隱式地傳遞生命周期,而不需要添加額外的調(diào)用或依賴。
當你在ViewModel里需要一個Lifecycle時,一個轉(zhuǎn)化可能是一種解決方案。
例如,假設我們有一個UI界面,用戶輸入地址并接收地址的郵政編碼。UI界面原始的ViewModel是這樣的:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository;
}
private LiveData<String> getPostalCode(String address) {
// 不要這樣做!
return repository.getPostCode(address);
}
}
實現(xiàn)如上,UI可能需要從之前的LiveData反注銷并在每次調(diào)用getPostalCode()新的實例時重新注冊。此外,如果UI是重新創(chuàng)建的,它出發(fā)了另一個調(diào)用repository.getPostCode(),而不是之前的結(jié)果。
作為上述方法的替換,你可以將郵政編碼信息作為地址信息輸入的轉(zhuǎn)換:
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,因為它永遠不會改變。它被定義為addressInput的轉(zhuǎn)化,因此當addressInput變化的時候,如果有一個激活的觀察者,repository.getPostCode()會被調(diào)用。如果沒有激活的觀察者,則不會有任何計算發(fā)生,直到添加了一個觀察者。
這種機制允許下層的應用創(chuàng)建LiveData對象,在需要的時候才計算。ViewModel可以輕易地獲取它們并在上層定義轉(zhuǎn)化規(guī)則。