感知生命週期的數據 -- LiveData

感知生命週期的數據 -- LiveData

零. 前言

上篇文章《萬物基於Lifecycle》 介紹了整個Lifecycle體系的基石,今天這篇文章我們來看看Jetpack給咱們帶來的活着的數據——LiveDatajava

大綱android

  • LiveData 是什麼?
  • 爲何要用LiveData?
  • How to use LiveData?
  • LiveData的生命感知能力從何而來,是如何與Lifecycle結合的?

一. 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

  1. 須要自行判斷宿主活躍狀態,防止生命週期越界。
  2. 若是Activity不可見,此時不更新UI,那麼就須要複寫onResume方法去更新UI,手工管理生命週期,增長了代碼的複雜性。

二. 爲何要使用LiveData?

上面的例子已經很好地詮釋了LiveData的強大,固然不只限於此,它的優點以下:工具

  1. 確保界面符合數據狀態

    LiveData 遵循觀察者模式。當生命週期狀態發生變化時,LiveData 會通知 Observer 對象。觀察者能夠在onChanged事件時更新界面,而不是在每次數據發生更改時當即更新界面。

  2. 不會發生內存泄漏

    觀察者會綁定到 Lifecycle 對象,並在其關聯的生命週期遭到銷燬後進行自我清理。

  3. 不會因 Activity 中止而致使崩潰

    若是觀察者的生命週期處於非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。

  4. 再也不須要手動處理生命週期

    界面組件只是觀察相關數據,不會中止或恢復觀察。LiveData 將自動管理全部這些操做,由於它在觀察時能夠感知相關的生命週期狀態變化。

  5. 數據始終保持最新狀態

    若是生命週期變爲非活躍狀態,它會在再次變爲活躍狀態時接收最新的數據。例如,曾經在後臺的 Activity 會在返回前臺後當即接收最新的數據。

  6. 適當的配置更改

    若是因爲配置更改(如設備旋轉)而從新建立了 Activity 或 Fragment,它會當即接收最新的可用數據。

  7. 共享資源

    可使用單一實例模式擴展 LiveData 對象以封裝系統服務,以便在應用中共享它們。LiveData 對象鏈接到系統服務一次,而後須要相應資源的任何觀察者只需觀察 LiveData 對象

  8. 支持黏性事件的分發
    即先發送一條數據,後註冊一個觀察者,默認是可以收到以前發送的那條數據的

三. How to use 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值的變化來作對應的事情,且不用擔憂生命週期越界的問題。

LiveData核心方法

方法名 做用
observe(LifecycleOwner owner,Observer observer) 註冊和宿主生命週期關聯的觀察者
observeForever(Observer observer) 註冊觀察者,不會反註冊,需自行維護
setValue(T data) 發送數據,沒有活躍的觀察者時不分發。只能在主線程。
postValue(T data) 和setValue同樣。不受線程環境限制,
onActive 當且僅當有一個活躍的觀察者時會觸發
inActive 不存在活躍的觀察者時會觸發

LiveData的衍生類及功能

  • MutableLiveData

    該類十分簡單,主要開放了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對象既能發消息也能收消息的混亂使用。

  • MediatorLiveData

    合併多個LiveData, 即一對多統一觀察,一個經典的場景是:在向服務器請求數據時,優先展現本地數據庫的數據,而後由請求的響應決定是否要更新數據庫,以下圖所示:

    img

    // 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

  • Transformations

    這是一個數據轉化工具類,共兩個主要方法:

    1. 靜態轉化 -- map(@NonNull LiveData source, @NonNull final Function<X, Y> mapFunction)

      MutableLiveData<Integer> data = new MutableLiveData<>();
      
      //數據轉換
      LiveData<String> transformData = Transformations.map(data, input ->   String.valueOf(input));
      //使用轉換後生成的transformData去觀察數據
      transformData.observe( this, output -> {
      
      });
      
      //使用原始的livedata發送數據
      data.setValue(10);
    2. 動態轉化 -- LiveData switchMap(
      @NonNull LiveData source,
      @NonNull final Function<X, LiveData > switchMapFunction)

    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);
    }

四. LiveData的實現機制

livedata2

LiveData註冊觀察者觸發消息分發流程:

  1. observe 註冊時,能夠主動跟宿主生命週期綁定,不用反註冊:
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);
}
  1. LifecycleBoundObserver 監聽宿主的生命週期(這裏咱們還記得以前在Lifecycle解析中提到addObserver時也會對observer進行包裝,這時同樣的),而且宿主不可見時不分發任何數據:
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());
        }
    }
  1. ObserverWrapper 狀態變動後,若是觀察者處於活躍狀態會觸發數據的分發流程:
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);
            }
        }
}
  1. dispatchingValue 數據分發流程控制:
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;
    }
  1. considerNotify 數據真正分發的地方,須要知足三個套件:
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 纔會觸發消息的分發:

img

五. 總結

Android開發大部分主要的工做就是從服務器獲取數據,將其轉爲渲染爲UI展現給用戶。本質上咱們所作的邏輯都是「數據驅動」,全部的View都是數據的一種狀態,數據映射的過程當中咱們須要去考慮生命週期的限制--即數據的活躍性。LiveData結合Lifecycle幫咱們規範了這個流程,完美詮釋了observer、lifecycle-aware、data holder 這個鐵三角,開發者在遵循這個開發流程的過程當中,便完成了UI -> ViewModel -> Data的單項依賴。

相關文章
相關標籤/搜索