天天看點

Android Jetpack架構元件(四)之LiveData一、 LiveData簡介二、 LiveData與ViewModel的關系三、 LiveData的基本使用四、LiveData工作原理

一、 LiveData簡介

LiveData是Jetpack架構元件Lifecycle 庫的一部分,是一個可感覺生命周期的可觀察容器類 (Observable)。與正常的可觀察類不同,LiveData 具有生命周期感覺能力,這意味着它具有感覺應用元件(如 Activity、Fragment 或 Service)的生命周期的能力,并且LiveData僅更新處于活躍生命周期狀态的應用元件觀察者。

是以,LiveData具有如下一些特效。

  • LiveData是一個持有資料的容器類,它持有的資料是可以被觀察者訂閱的,當資料發生變化時會通知觀察者,觀察者可以是 Activity、Fragment、Service 等對象。
  • LiveData 具有感覺觀察者的生命周期能力,并且隻有當觀察者處于激活狀态(STARTED、RESUMED)才會接收到資料更新的通知,在未激活時會自動解除注冊觀察者,以降低記憶體洩漏的風險。
  • 使用 LiveData 儲存資料時,由于資料群組件是分離的,是以當元件被銷毀時可以保證資料不會丢失。

是以,我們認為LiveData就是一個資料容器,它負責将資料包裹起來,使資料成為被觀察者,當資料發生變化時,LiveData會通知觀察者以便觀察者做出響應。

那相比其他的一些觀察者技術,如RxJava什麼的,LiveData有哪些優勢嗎,下面是官方給出的一些優點列舉。

  • 確定 UI 界面始終和資料狀态保持一緻。
  • 不會發生記憶體洩漏。觀察者綁定到 Lifecycle 對象并在其相關生命周期 destroyed 後自行解除綁定。
  • 不會因為 Activity 停止而發生奔潰。如 Activity執行finish方法後,它就不會收到任何LiveData 事件。
  • 不再需要手動處理生命周期。UI 元件隻需觀察相關資料,不需要停止或恢複觀察,LiveData 會自動管理這些操作,因為 LiveData 可以感覺生命周期狀态的更改。
  • 資料始終保持最新狀态。在生命周期從非激活狀态變為激活狀态,始終保持最新資料,如背景 Activity 在傳回到前台後可以立即收到資料的最新狀态。
  • 适當的配置更改。當配置發生更改(如螢幕旋轉)而重建 Activity / Fragment,它會立即收到最新的可用資料。
  • 資源共享。LiveData 很适合用于元件(Activity / Fragment)之間的通信,以及共享資料資源。

二、 LiveData與ViewModel的關系

在Jetpack架構中,ViewModel的主要作用是存儲各種資料,當然,我們也可以在ViewModel中處理一些資料邏輯。例如,我們可以在ViewModel中對加載的資料進行某些加工操作。

而對頁面來說,它并不需要關心ViewModel中的資料邏輯,它隻關心需要展示的資料是什麼,并且在資料發生變化時通知頁面資料的變化并做出相應的更新。而LiveData的作用就是包裝ViewModel中資料,并讓被觀察者能夠觀察資料的變化。下圖是官方Jetpack架構的示意圖。

Android Jetpack架構元件(四)之LiveData一、 LiveData簡介二、 LiveData與ViewModel的關系三、 LiveData的基本使用四、LiveData工作原理

在這裡插入圖檔描述

三、 LiveData的基本使用

3.1 使用步驟

LiveData的使用比較簡單,主要會涉及以下幾個步驟:

  • 建立 LiveData 執行個體以存儲某種類型的資料,通常在ViewModel中完成。
  • 定義一個具有onChanged()方法的Observer對象,當LiveData持有資料發生變化時回調該方法。通常,我們可以在UI控制器類中建立該Observer對象,如Activity或Fragment。
  • 通過使用observe()方法将上述的LiveData對象和Observer對象關聯在一起。這樣Observer對象就與LiveData産生了訂閱關系,當LiveData資料發生變化時通知,而在Observer更新資料,是以Observer通常是Activity和Fragment。

從上述步驟可以看出,LiveData使用了觀察者模式,觀察者通常是UI控制器,如Activity或Fragment,而被觀察者則是LiveData包谷的資料對象, 當LiveData對象持有資料發生變化,會通知對它訂閱的所有處于活躍狀态的訂閱者。

3.2 LiveData使用示例

3.2.1 建立 LiveData 對象

LiveData是一種可用于任何資料的封裝容器,其中包括可實作 Collections 的對象,如 List。LiveData 對象通常存儲在 ViewModel 對象中,并可通過 getter 方法進行通路,如下所示。

public class NameViewModel extends ViewModel {

    private MutableLiveData<String> name;
    public MutableLiveData<String> getName() {
        if (name == null) {
            name = new MutableLiveData<String>();
        }
        return name;
    }
}           

複制

3.2.2 觀察 LiveData 對象

在大多數情況下,我們可以應用元件的 onCreate() 方法中開始觀察 LiveData 對象。并且,LiveData 僅在資料發生更改時才發送更新,并且僅發送給活躍觀察者,如下所示。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private NameViewModel model;
    private TextView nameTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        nameTV=findViewById(R.id.nameTV);
        model = new ViewModelProvider(this).get(NameViewModel.class);
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                nameTV.setText(newName);
            }
        };
        model.getName().observe(this, nameObserver);
    }
}           

複制

當我們傳遞 nameObserver 參數的情況下調用 observe() 後,系統會立即調用 onChanged(),進而提供 mCurrentName 中存儲的最新值,如果 LiveData 對象尚未在 mCurrentName 中設定值,則不會調用 onChanged()。事實上,最簡單的LiveData使用方法是MutableLiveData,如下所示。

public class MainActivity extends AppCompatActivity {
   private static final String TAG="MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MutableLiveData<String> mutableLiveData  = new MutableLiveData<>();
        mutableLiveData.observe(this, new Observer<String>() { 
            @Override
            public void onChanged(@Nullable final String s) {
                Log.d(TAG, "onChanged:"+s);
            }
        });
        mutableLiveData.postValue("Android應用開發實戰");
    }
}           

複制

3.2.3 更新 LiveData 對象

LiveData 本身沒有公開可用的方法來更新存儲的資料,如果需要修改LiveData的資料,可以使用MutableLiveData 類将公開 setValue(T) 和 postValue(T) 方法。通常情況下會在 ViewModel 中使用 MutableLiveData,然後 ViewModel 隻會向觀察者公開不可變的 LiveData 對象,如下所示。

button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            String anotherName = "John Doe";
            model.getCurrentName().setValue(anotherName);
        }
    });
              

複制

3.2.4 擴充 LiveData

如果觀察者的生命周期處于 STARTED 或 RESUMED 狀态,則 LiveData 會認為該觀察者處于活躍狀态。以下示例代碼說明了如何擴充 LiveData 類。

public class StockLiveData extends LiveData<BigDecimal> {
        private StockManager stockManager;

        private SimplePriceListener listener = new SimplePriceListener() {
            @Override
            public void onPriceChanged(BigDecimal price) {
                setValue(price);
            }
        };

        public StockLiveData(String symbol) {
            stockManager = new StockManager(symbol);
        }

        @Override
        protected void onActive() {
            stockManager.requestPriceUpdates(listener);
        }

        @Override
        protected void onInactive() {
            stockManager.removeUpdates(listener);
        }
    }
               

複制

在上面的示例中,我們首先建立一個StockLiveData并繼承自LiveData,并重寫了onActive和onInactive兩個重要方法。

  • onActivite():當有活躍狀态的訂閱者訂閱LiveData時會回調該方法,意味着需要在這裡監聽資料的變化。
  • onInactive():當沒有活躍狀态的訂閱者訂閱LiveData時會回調該方法,此時沒有必要保持StockManage服務象的連接配接。
  • setValue():注意到value=price這裡是調用了setValue(price)方法,通過該方法更新LiveData的值,進而通知處于活躍狀态的訂閱者。

此時,LiveData會認為訂閱者的生命周期處于STARTED或RESUMED狀态時,該訂閱者是活躍的,那麼如何使用 StockLiveData 類呢,如下所示。

public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            LiveData<BigDecimal> myPriceListener = ...;
            myPriceListener.observe(getViewLifeycleOwner(), price -> {
                // Update the UI.
            });
        }
    }
              

複制

以Fragment作LifecycleOwner的執行個體傳遞到observer()方法中,這樣就将Observer綁定到擁有生命周期的擁有者。由于LiveData可以在多個Activity、Fragment和Service中使用,是以可以建立單例模式。

public class StockLiveData extends LiveData<BigDecimal> {
        private static StockLiveData sInstance;
        private StockManager stockManager;

        private SimplePriceListener listener = new SimplePriceListener() {
            @Override
            public void onPriceChanged(BigDecimal price) {
                setValue(price);
            }
        };

        @MainThread
        public static StockLiveData get(String symbol) {
            if (sInstance == null) {
                sInstance = new StockLiveData(symbol);
            }
            return sInstance;
        }

        private StockLiveData(String symbol) {
            stockManager = new StockManager(symbol);
        }

        @Override
        protected void onActive() {
            stockManager.requestPriceUpdates(listener);
        }

        @Override
        protected void onInactive() {
            stockManager.removeUpdates(listener);
        }
    }
               

複制

然後,我們就可以在 Fragment 中使用它,如下所示。

public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
                // Update the UI.
            });
        }
    }
               

複制

3.2.5 轉換 LiveData

有時候,我們希望在把資料分發給觀察者之前進行一些處理,或者傳回一個基于已有值的LiveData對象的另外一個LiveData對象,此時就會用到 Transformations類。轉化LiveData時需要用到Transformations.map()和Transformations.switchMap()等方法。

Transformations.map()

例如,下面是使用Transformations.map()方法處理LiveData存儲的資料,然後将其傳遞給下遊的示例代碼。

LiveData<User> userLiveData = ...;
    LiveData<String> userName = Transformations.map(userLiveData, user -> {
        user.name + " " + user.lastName
    });
               

複制

Transformations.switchMap()

使用Transformations.switchMap()方法同樣可以改變LiveData下遊的結果,但傳遞給switchMap()函數的必須是一個LiveData對象,如下所示。

private LiveData<User> getUser(String id) {
      ...;
    }

    LiveData<String> userId = ...;
    LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
               

複制

不過,這種轉換方式是惰性的,也就是隻有Observer來訂閱資料的時候,才會進行轉換。是以,當在ViewModel中使用一個Lifecycle對象,這種轉換是一種很好的解決方案。例如,假設您有一個界面元件,該元件接受位址并傳回該位址的郵政編碼,那麼我們可以使用switchMap()方法進行轉化。

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

複制

四、LiveData工作原理

通過前面的介紹,我們知道LiveData是一個可觀察的資料持有者,并且它是具有元件生命周期感覺能力的,那它是如何觀察元件生命周期變化的呢?同時,LiveData僅更新處于活躍生命周期狀态的應用元件觀察者,也即是說LiveData并不會通知所有的觀察者,它隻會通知處于活躍狀态的觀察者,那麼它是如何做到這一點的呢?

LiveData生命周期變化觀察

前面介紹LiveData用法的時候提到,首先,我們建立 LiveData 執行個體,然後調用LiveData的observe方法來注冊觀察者,将ViewModel和LiveData關聯起來。observe()方法的源碼如下。

@MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }           

複制

可以發現,observe首先會判斷元件目前的狀态,如果狀态為DESTROYED,那麼直接return,這說明DESTROYED狀态的元件是不允許注冊的。然後,建立一個LifecycleBoundObserver包裝類,将owner和observer傳了進去。接下來,将observer和LifecycleBoundObserver存儲到

SafeIterableMap<Observer<? super T>, ObserverWrapper>mObservers

中,注意,此處使用的是putIfAbsent方法,接下來對傳入的值進行判斷,如果傳入key對應的value已經存在,就傳回存在的value,不進行替換,如果不存在就添加key和value,傳回null。

最後,通過

owner.getLifecycle().addObserver()

方法将LifecycleBoundObserver添加到Lifecycle中完成注冊,這樣處理之後LiveData就有了觀察元件生命周期變化的能力。

LifecycleBoundObservers

LifecycleBoundObservers是LiveData的内部類,源碼如下。

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }           

複制

LifecycleBoundObserver實作了GenericLifecycleObserver接口,當元件狀态發生變化時,會調用onStateChanged方法,當元件處于DESTROYED狀态時,會調用removeObserver方法移除observer。

而LifecycleBoundObserver繼承了ObserverWrapper類,需要重寫shouldBeActive方法,用于判斷目前傳入的元件的狀态是否是Active狀态,即處于STARTED和RESUMED狀态。

ObserverWrapper

ObserverWrapper是Observer的包裝類,LiveData的生命周期狀态監聽activeStateChanged方法就定義在抽象類ObserverWrapper中,源碼如下。

private abstract class ObserverWrapper {
      final Observer<? super T> mObserver;
      boolean mActive;
      int mLastVersion = START_VERSION;

      ObserverWrapper(Observer<? super T> observer) {
          mObserver = observer;
      }

      abstract boolean shouldBeActive();

      boolean isAttachedTo(LifecycleOwner owner) {
          return false;
      }

      void detachObserver() {
      }

      void activeStateChanged(boolean newActive) {
          if (newActive == mActive) {
              return;
          }
          mActive = newActive;
          boolean wasInactive = LiveData.this.mActiveCount == 0;
          LiveData.this.mActiveCount += mActive ? 1 : -1;
          if (wasInactive && mActive) {
              onActive();
          }
          if (LiveData.this.mActiveCount == 0 && !mActive) {
              onInactive();
          }
          if (mActive) {
              dispatchingValue(this);
          }
      }
  }           

複制

activeStateChanged()方法會根據Active狀态和處于Active狀态的元件的數量,來對onActive方法和onInactive方法進行回調,我們可以使用這兩個方法拓展LiveData對象。如果生命周期狀态是Active狀态,那麼會調用dispatchingValue方法。

private void dispatchingValue(@Nullable ObserverWrapper initiator) {
      if (mDispatchingValue) {
          mDispatchInvalidated = true;
          return;
      }
      mDispatchingValue = true;
      do {
          mDispatchInvalidated = false;
          if (initiator != null) {
              considerNotify(initiator);
              initiator = null;
          } else {
              for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                      mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                  considerNotify(iterator.next().getValue());
                  if (mDispatchInvalidated) {
                      break;
                  }
              }
          }
      } while (mDispatchInvalidated);
      mDispatchingValue = false;
  }           

複制

mDispatchingValue用于标記目前是否處于分發狀态中,如果處于分發狀态就會進行狀态的分發,并最終調用considerNotify方法進行消息的分發,代碼如下所示。

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) { 
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false); 
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData); 
}           

複制

considerNotify方法中做了多次的判斷,首先,判斷ObserverWrapper的mActive值如果不為true就直接return。然後,判斷目前observer對應元件的狀态是不是Active,如果不是就會再次調用activeStateChanged方法并傳入false,其方法内部會再次判斷是否執行onActive方法和onInactive方法回調。如果判斷條件都滿足就繼續調用Observer的onChanged方法,這個方法正是使用LiveData的observe方法的回調。