[譯] Architecture Components 之 LiveData

【目錄】

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 能夠指定一個其應該遵照的 Lifecyclejavascript

注:在 Android 項目中導入 LiveData,請參閱添加組件到項目中html

若是 ObserverLifecycle 處於 STARTED 或 RESUMED 狀態,LiveData 會認爲 Observer 處於活動狀態。java

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 個重要部分:android

onActive()git

LiveData 有一個處於活動狀態的觀察者時該方法被調用,這意味着須要開始從設備觀察位置更新。github

onInactive()app

LiveData 沒有任何處於活動狀態的觀察者時該方法被調用。因爲沒有觀察者在監聽,因此沒有理由保持與 LocationManager 的鏈接。這是很是重要的,由於保持鏈接會顯著消耗電量而且沒有任何好處。ide

setValue()post

調用該方法更新 LiveData 實例的值,並將此變動通知給處於活動狀態的觀察者。ui

能夠像下面這樣使用新的 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,意思是:

  • 若是 Lifecycle 不處於活動狀態(STARTED 或 RESUMED),即便該值發生變化也不會調用觀察者。

  • 若是 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 中止而崩潰:若是 ObserverLifecycle 處於閒置狀態(例如:activity 在後臺時),它們不會收到變動事件。

  • 始終保持數據最新:若是 Lifecycle 從新啓動(例如:activity 從後臺返回到啓動狀態)將會收到最新的位置數據(除非尚未)。

  • 正確處理配置更改:若是 activity 或 fragment 因爲配置更改(如:設備旋轉)從新建立,將會當即收到最新的有效位置數據。

  • 資源共享:能夠只保留一個 MyLocationListener 實例,只鏈接系統服務一次,而且可以正確的支持應用程序中的全部觀察者。

  • 再也不手動管理生命週期你可能已經注意到,fragment 只是在須要的時候觀察數據,不用擔憂被中止或者在中止以後啓動觀察。因爲 fragment 在觀察數據時提供了其 Lifecycle,因此 LiveData 會自動管理這一切。

LiveData 的轉換

有時候可能會須要在將 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() 的方法必須返回一個 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() 將會被調用。若是在調用時沒有處於活動狀態的觀察者,在添加觀察者以前不會進行任何運算。

該機制容許以較少的資源根據須要惰性運算來建立 LiveDataViewModel 能夠輕鬆獲取到 LiveData 並在它們上面定義轉換規則。

建立新的轉換

在應用程序中可能會用到十幾種不一樣的特定轉換,可是默認是不提供的。可使用 MediatorLiveData 實現本身的轉換,MediatorLiveData 是爲了用來正確的監聽其它 LiveData 實例並處理它們發出的事件而特別建立的。MediatorLiveData 須要特別注意正確的向源 LiveData 傳遞其處於活動/閒置狀態。有關詳細信息,請參閱 Transformations 類。

相關文章
相關標籤/搜索