【目錄】
1. Architecture Components 之 Guide to App Architecture
2. Architecture Components 之 Adding Components to your Project
3. Architecture Components 之 Handling Lifecycles
4. Architecture Components 之 LiveData
5. Architecture Components 之 ViewModel
6. Architecture Components 之 Room Persistence Library
示例程式碼連結
LiveData
LiveData 是一個資料持有者類,它持有一個值並允許觀察該值。不同於普通的可觀察者,LiveData 遵守應用程式元件的生命週期,以便 Observer 可以指定一個其應該遵守的 Lifecycle。
注:在 Android 專案中匯入 LiveData,請參閱新增元件到專案中
如果 Observer 的 Lifecycle 處於 STARTED 或 RESUMED 狀態,LiveData 會認為 Observer 處於活動狀態。
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 監聽的實現有 3 個重要部分:
onActive()
當 LiveData 有一個處於活動狀態的觀察者時該方法被呼叫,這意味著需要開始從裝置觀察位置更新。
onInactive()
當 LiveData 沒有任何處於活動狀態的觀察者時該方法被呼叫。由於沒有觀察者在監聽,所以沒有理由保持與 LocationManager 的連線。這是非常重要的,因為保持連線會顯著消耗電量並且沒有任何好處。
setValue()
呼叫該方法更新 LiveData 例項的值,並將此變更通知給處於活動狀態的觀察者。
可以像下面這樣使用新的 LocationLiveData:
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
LiveData<Location> myLocationListener = ...;
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.addObserver(this, location -> {
// 更新 UI
});
}
});
}
}複製程式碼
請注意,addObserver() 方法將 LifecycleOwner 作為第一個引數傳遞。這樣做表示該觀察者應該繫結到 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);
}
}複製程式碼
現在 fragment 可以像下面這樣使用它:
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
Util.checkUserStatus(result -> {
if (result) {
LocationLiveData.get(getActivity()).observe(this, location -> {
// update UI
});
}
});
}
}複製程式碼
可能會有多個 fragment 和 activity 在觀察 MyLocationListener 例項,LiveData 可以規範的管理它們,以便只有當它們中的任何一個可見(即處於活動狀態)時才連線到系統服務。
LiveData 有以下優點:
沒有記憶體洩漏:因為 Observer 被繫結到它們自己的 Lifecycle 物件上,所以,當它們的 Lifecycle 被銷燬時,它們能自動的被清理。
不會因為 activity 停止而崩潰:如果 Observer 的 Lifecycle 處於閒置狀態(例如:activity 在後臺時),它們不會收到變更事件。
始終保持資料最新:如果 Lifecycle 重新啟動(例如:activity 從後臺返回到啟動狀態)將會收到最新的位置資料(除非還沒有)。
正確處理配置更改:如果 activity 或 fragment 由於配置更改(如:裝置旋轉)重新建立,將會立即收到最新的有效位置資料。
資源共享:可以只保留一個 MyLocationListener 例項,只連線系統服務一次,並且能夠正確的支援應用程式中的所有觀察者。
不再手動管理生命週期你可能已經注意到,fragment 只是在需要的時候觀察資料,不用擔心被停止或者在停止之後啟動觀察。由於 fragment 在觀察資料時提供了其 Lifecycle,所以 LiveData 會自動管理這一切。
LiveData 的轉換
有時候可能會需要在將 LiveData 傳送到觀察者之前改變它的值,或者需要更具另一個 LiveData 返回一個不同的 LiveData 例項。
Lifecycle 包提供了一個 Transformations 類包含對這些操作的幫助方法。
-
在 LiveData 的值上應用一個方法,並將結果傳遞到下游。
LiveData<User> userLiveData = ...; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });複製程式碼
-
與 map() 類似,將一個方法應用到 LiveData 的值並解包,然後將結果傳遞到下游。傳遞給 switchMap() 的方法必須返回一個 Lifecycle
private LiveData<User> getUser(String id) { ...; } LiveData<String> userId = ...; LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );複製程式碼
使用這些轉換允許在整個呼叫鏈中攜帶觀察者的 Lifecycle 資訊,以便只有在觀察者觀察到 LiveData 的返回時才運算這些轉換。轉換的這種惰性運算性質允許隱式的傳遞生命週期相關行為,而不必新增顯式的呼叫或依賴。
每當你認為在 ViewModel 中需要一個 Lifecycle 類時,轉換可能是解決方案。
例如:假設有一個 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);
}
}複製程式碼
如果是像這種實現,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 欄位為 public final,因為它永遠不會改變。postalCode 被定義為 addressInput 的轉換,所以當 addressInput 改變時,如果有處於活動狀態的觀察者,repository.getPostCode() 將會被呼叫。如果在呼叫時沒有處於活動狀態的觀察者,在新增觀察者之前不會進行任何運算。
該機制允許以較少的資源根據需要惰性運算來建立 LiveData。ViewModel 可以輕鬆獲取到 LiveData 並在它們上面定義轉換規則。
建立新的轉換
在應用程式中可能會用到十幾種不同的特定轉換,但是預設是不提供的。可以使用 MediatorLiveData 實現自己的轉換,MediatorLiveData 是為了用來正確的監聽其它 LiveData 例項並處理它們發出的事件而特別建立的。MediatorLiveData 需要特別注意正確的向源 LiveData 傳遞其處於活動/閒置狀態。有關詳細資訊,請參閱 Transformations 類。