上篇文章《萬物基於Lifecycle》 介紹了整個Lifecycle體系的基石,今天這篇文章我們來看看Jetpack給咱們帶來的活着的數據——LiveData。java
大綱android
一. LiveData 是什麼?數據庫
LiveData 簡單來講,就是普通數據對象的一個包裝類,這個包裝類中幫助你主動管理了數據的版本號,基於觀察者模式,讓普通的數據對象可以感知所屬宿主(Activity、Fragment)的生命週期。這種感知能力就可以保證只有宿主活躍(Resumed、Started)時,數據的觀察者才能受到數據變化的消息。api
上面這短短的一段話,卻有着重大的意義,舉一個case:服務器
有一個頁面須要加載一個列表,咱們須要在後臺線程去服務器請求對應的數據,請求數據成功並通過解析後post消息通知UI,UI再渲染請求來的數據。等等!倘若此時的網絡很慢,而恰好用戶此時按home鍵退出了應用,那麼在此時UI是不該該被渲染的,而是應該等用戶從新進入應用時纔開始渲染。網絡
從下面的演示代碼就詮釋了上面的說所的case架構
class MainActivity extends AppcompactActivity{ public void onCreate(Bundle bundle){ Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { //不管頁面可見不可見,都會去執行頁面刷新,IO。更有甚者彈出對話框 } }; //1.不管當前頁面是否可見,這條消息都會被分發。----消耗資源 //2.不管當前宿主是否還存活,這條消息都會被分發。---內存泄漏 handler.sendMessage(msg) liveData.observer(this,new Observer<User>){ void onChanged(User user){ } } //1.減小資源佔用--- 頁面不可見時不會派發消息 //2.確保頁面始終保持最新狀態---頁面可見時,會馬上派發最新的一條消息給全部觀察者--保證頁面最新狀態 //3.再也不須要手動處理生命週期---避免NPE //4.能夠打造一款不用反註冊,不會內存泄漏的消息總線---取代eventbus liveData.postValue(data); } }
有人說,我能夠在處理消息時,根據當前頁面時是否可見來具體處理對應邏輯。是的,沒錯,確實能夠這樣,就像下面這樣。app
Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { if (isActivityValid()) { // updateUI... } else { // dosomething... } } };
我再拿上面的例子說一下這種問題:ide
上面的例子已經很好地詮釋了LiveData的強大,固然不只限於此,它的優點以下:工具
確保界面符合數據狀態
LiveData 遵循觀察者模式。當生命週期狀態發生變化時,LiveData 會通知 Observer
對象。觀察者能夠在onChanged事件時更新界面,而不是在每次數據發生更改時當即更新界面。
不會發生內存泄漏
觀察者會綁定到 Lifecycle
對象,並在其關聯的生命週期遭到銷燬後進行自我清理。
不會因 Activity 中止而致使崩潰
若是觀察者的生命週期處於非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。
再也不須要手動處理生命週期
界面組件只是觀察相關數據,不會中止或恢復觀察。LiveData 將自動管理全部這些操做,由於它在觀察時能夠感知相關的生命週期狀態變化。
數據始終保持最新狀態
若是生命週期變爲非活躍狀態,它會在再次變爲活躍狀態時接收最新的數據。例如,曾經在後臺的 Activity 會在返回前臺後當即接收最新的數據。
適當的配置更改
若是因爲配置更改(如設備旋轉)而從新建立了 Activity 或 Fragment,它會當即接收最新的可用數據。
共享資源
可使用單一實例模式擴展 LiveData
對象以封裝系統服務,以便在應用中共享它們。LiveData
對象鏈接到系統服務一次,而後須要相應資源的任何觀察者只需觀察 LiveData
對象
支持黏性事件的分發
即先發送一條數據,後註冊一個觀察者,默認是可以收到以前發送的那條數據的
step1: 添加依賴:
step2: 在ViewModel中建立 LiveData
實例 (ViewModel組件會在下期講到,將LiveData存儲至viewModel中,是爲了符合MVVM架構思想,V層僅負責展現,而VM層負責數據邏輯)
public class ViewModelTest extends AndroidViewModel { public final MutableLiveData<String> name = new MutableLiveData<>(); ... }
step3: 在Activity或Fragment中對LiveData進行添加觀察者進行監聽
public class ActivityTest extends AppCompatActivity { ... @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... viewModel.name.observe(this, new Observer<String>() { @Override public void onChanged(@Nullable String name) { // dosomething... } }); } }
咱們能夠根據LiveData值的變化來作對應的事情,且不用擔憂生命週期越界的問題。
方法名 | 做用 |
---|---|
observe(LifecycleOwner owner,Observer observer) | 註冊和宿主生命週期關聯的觀察者 |
observeForever(Observer observer) | 註冊觀察者,不會反註冊,需自行維護 |
setValue(T data) | 發送數據,沒有活躍的觀察者時不分發。只能在主線程。 |
postValue(T data) | 和setValue同樣。不受線程環境限制, |
onActive | 當且僅當有一個活躍的觀察者時會觸發 |
inActive | 不存在活躍的觀察者時會觸發 |
該類十分簡單,主要開放了LiveData的發消息接口
public class MutableLiveData<T> extends LiveData<T> { @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); } }
設計初衷:考慮單一開閉原則,LiveData只能接受消息,避免拿到LiveData對象既能發消息也能收消息的混亂使用。
合併多個LiveData, 即一對多統一觀察,一個經典的場景是:在向服務器請求數據時,優先展現本地數據庫的數據,而後由請求的響應決定是否要更新數據庫,以下圖所示:
// ResultType: Type for the Resource data. // RequestType: Type for the API response. public abstract class NetworkBoundResource<ResultType, RequestType> { // MediatorLiveData 數據組合者 private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>(); private Executor executor; @MainThread protected NetworkBoundResource(Executor mExecutor) { this.executor = mExecutor; // 首先初始化一個Loading的status 空result result.setValue(Resource.loading(null)); // 而後從數據庫中獲取持久化數據 LiveData<ResultType> dbSource = loadFromDb(); // 數據組合者監聽數據庫中的數據 result.addSource(dbSource, data -> { // dbSource第一次回調,用來判斷數據有效期,此時取消監聽 result.removeSource(dbSource); // 業務自行定義是否須要fetch最新的數據 if (shouldFetch(data)) { fetchFromNetwork(dbSource); } else { // 數據有效,從新觀察一次,觀察者會立馬收到一次回調(LiveData粘性事件機制) result.addSource(dbSource, newData -> result.setValue(Resource.success(newData))); } }); } private void fetchFromNetwork(final LiveData<ResultType> dbSource) { LiveData<ApiResponse<RequestType>> apiResponse = createCall(); // 這裏數據雖無效,可是能夠先給UI展現 result.addSource(dbSource, newData -> setValue(Resource.loading(newData))); result.addSource(apiResponse, response -> { result.removeSource(apiResponse); result.removeSource(dbSource); if (response != null) { if (response.isSuccessful()) { executor.execute(() -> { saveCallResult(processResponse(response)); executor.execute(() -> // 這裏咱們拿到的最新的數據須要主動通知監聽者,以拿到從服務端拿到的最新數據 result.addSource(loadFromDb(), newData -> setValue(Resource.success(newData))) ); }); } else { onFetchFailed(); result.addSource(dbSource, newData -> setValue(Resource.error(response.errorMessage, newData))); } } else { result.addSource(dbSource, newData -> setValue(Resource.error("Request failed, server didn't response", newData))); } }); } }
上面的例子MediatorLiveData同時監聽了數據庫中的LiveData和服務端的LiveData
這是一個數據轉化工具類,共兩個主要方法:
靜態轉化 -- map(@NonNull LiveData
MutableLiveData<Integer> data = new MutableLiveData<>(); //數據轉換 LiveData<String> transformData = Transformations.map(data, input -> String.valueOf(input)); //使用轉換後生成的transformData去觀察數據 transformData.observe( this, output -> { }); //使用原始的livedata發送數據 data.setValue(10);
動態轉化 -- LiveData
@NonNull LiveData
@NonNull final Function<X, LiveData
MutableLiveData userIdLiveData = ...; // 用戶數據和用戶id緊密相關,當咱們改變userId的liveData的同時還會主動通知userLiveData更新 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id -> repository.getUserById(id)); void setUserId(String userId) { this.userIdLiveData.setValue(userId); } // 不要像下面這麼作 private LiveData getUserLiveData(String userId) { // DON'T DO THIS return repository.getUserById(userId); }
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { //1. 首先來個斷言,這個方法只能在主線程調用,observeForever也是。 assertMainThread("observe"); //2.其次把註冊進來的observer包裝成 一個具備生命周邊邊界的觀察者 //它能監聽宿主被銷燬的事件,從而主動的把本身反註冊,避免內存泄漏 //此時觀察者是否處於活躍狀態就等於宿主是否可見 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //3.接着會判斷該觀察是否已經註冊過了,若是是則拋異常,因此要注意,不容許重複註冊 ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } //4.這一步纔是關鍵 //利用Lifecycle,把觀察者註冊到進去,才能監聽到宿主生命週期狀態的變化,對不對? //根據Lifecycle文章中的分析,一旦一個新的觀察者被添加,Lifecycle也會同步它的狀態和宿主一致對不對?此時會觸發觀察者的onStateChanged方法 owner.getLifecycle().addObserver(wrapper); }
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) { super(observer); } @Override boolean shouldBeActive() { //使用observer方法註冊的觀察者都會被包裝成LifecycleBoundObserver //觀察者是否活躍就等於宿主 的狀態是否大於等於STARTED, //若是頁面當前不可見,你發送了一條消息,此時是不會被分發的,能夠避免後臺任務搶佔資源,當頁面恢復可見纔會分發。 //注意:若是使用observerForever註冊的觀察者, //會被包裝成AlwaysActiveObserver,它的shouldBeActive一致返回true.即使在頁面不可見也能收到數據 return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { //在這裏若是監聽到宿主被銷燬了,則主動地把本身從livedata的觀察者中移除掉 if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { removeObserver(mObserver); return; } //不然說明宿主的狀態發生了變化,此時會判斷宿主是否處於活躍狀態 activeStateChanged(shouldBeActive()); } }
abstract class ObserverWrapper{ final Observer<? super T> mObserver; boolean mActive; int mLastVersion = START_VERSION//這裏就等於-1,沒有主動和LiveData的mVersion對齊,爲黏性事件埋下了伏筆 void activeStateChanged(boolean newActive) { if (newActive == mActive) { return; } //更改觀察者的狀態 mActive = newActive; boolean wasInactive = LiveData.this.mActiveCount == 0; //若是此時有且只有一個活躍的觀察者則觸發onActive LiveData.this.mActiveCount += mActive ? 1 : -1; if (wasInactive && mActive) { onActive(); } //沒有任何一個活躍的觀察者則觸發onInactive //利用這個方法被觸發的時機,能夠作不少事,好比懶加載,資源釋放等 if (LiveData.this.mActiveCount == 0 && !mActive) { onInactive(); } //若是此時觀察者處於活躍狀態,下面就開始分發數據了 //請注意,這裏傳遞了this = observer if (mActive) { dispatchingValue(this); } } }
void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator != null) { //若是傳遞的觀察者不爲空,則把數據分發給他本身。這個流程是新註冊觀察者的時候會被觸發 considerNotify(initiator); initiator = null; } else { //不然遍歷集合中全部已註冊的的觀察者,逐個調用considerNotify,分發數據 for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; }
private void considerNotify(ObserverWrapper observer) { //觀察者當前狀態不活躍不分發 if (!observer.mActive) { return; } //觀察者所在宿主是否處於活躍狀態,不然不分發,而且更改觀察者的狀態爲false if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } //此處判斷觀察者接收消息的次數是否大於等於 發送消息的次數 //可是observer被建立之初verison=-1 //若是此時LiveData已經發送過數據了。這裏就不知足了,就出現黏性事件了,後註冊的觀察者收到了前面發送的消息。 if (observer.mLastVersion >= mVersion) { return; } //每分發一次消息,則把觀察者和LiveData的version對齊,防止重複發送 observer.mLastVersion = mVersion; //最後的數據傳遞 observer.mObserver.onChanged((T) mData); }
普通消息分發流程。即調用 postValue,setValue 纔會觸發消息的分發:
Android開發大部分主要的工做就是從服務器獲取數據,將其轉爲渲染爲UI展現給用戶。本質上咱們所作的邏輯都是「數據驅動」,全部的View都是數據的一種狀態,數據映射的過程當中咱們須要去考慮生命週期的限制--即數據的活躍性。LiveData結合Lifecycle幫咱們規範了這個流程,完美詮釋了observer、lifecycle-aware、data holder 這個鐵三角,開發者在遵循這個開發流程的過程當中,便完成了UI -> ViewModel -> Data的單項依賴。