【翻譯】安卓架構(gòu)組件(4)-LiveData

相關(guān)文章:

LiveData是一個數(shù)據(jù)持有類,保存了數(shù)據(jù)值以及允許該值被觀察。并不像常規(guī)的被觀察者,LiveData遵循app組件的生命周期,例如Observer可以指定要觀察的Lifecyle。

如果ObserverLifecycleSTARTED或者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ī)則。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容