在以前的文章中,咱們講了Android Architecture components 中的 Lifecycle 組件的詳細使用以及源碼解析。本篇將介紹另外AAC中另外兩個組件:LiveData 和 ViewModel,它們的實現也都是利用了 Lifecycle。android
LiveData 是一個可觀測的數據持有類,可是不一樣於一般的被觀察者,LiveData 具備生命週期感知能力。通俗點說,LiveData 就是具備 「Live」 能力的 「Data」 持有類。當它所持有的數據發生改變的時候,而且 Lifecycle 對象(好比 Activity 或者 Fragment 等)處於活躍狀態(STARTED 或者 RESUMED),LiveData 將當即通知觀察者數據發生了變化。也就是說,比普通觀察者多了個生命週期感知能力。數據庫
當數據發生改變的時候,會自動通知UI進行更新。緩存
Observers 是綁定到 Lifecycle 對象上的,當與其關聯的 lifecycle 被銷燬的時候,它們會自動被清理。網絡
當 Observer 所綁定的 Lifecycle 處於非活躍狀態時,好比處於返回棧中的 Activity,它將不會收到任何 LiveData 事件。app
UI 組件只須要對相關的數據進行監聽,不須要關心是否應該暫停或者恢復監聽。LiveData 具備生命週期感知能力,它會自動對這些進行管理。異步
若是一個 Lifecycle 處於非活躍狀態,那當它由非活躍狀態變爲活躍狀態的時候,它將收到最新的數據。好比一個 Activity 由後臺轉爲前臺,這時候它將當即收到最新的數據async
當 Activity 或者 Fragment 因爲配置更改而從新建立時(好比旋轉屏幕等),它將收到最新的可用數據。這裏簡單提一點,這個有點是須要配合 ViewModel 使用的,嚴格來講,它主要是 ViewModel 的優勢ide
咱們可使用單例模式來擴展 LiveData,這樣就能達到數據變化的時候,通知全部的觀察者。post
爲了便於理解,關於 LiveData 和 ViewModel 的關係,我這裏先說結論:學習
LiveData 的做用是在使得數據能具備生命週期感知能力,在 Activity 等變爲活躍狀態的時候,自動回調觀察者中的回調方法。也就是說對數據的變化進行實時監聽。而 ViewModel 的做用則是,當因系統配置發生改變致使 Activity 重建的時候(好比旋轉屏幕),能對 LiveData 進行正確的保存和恢復。僅此而已。
通常來說,LiveData 是須要配合 ViewModel 來使用的,但千萬不要以爲 LiveData 就必定結合 ViewModel。上面也說道兩者只是功能互補。這裏爲了便於理解,咱們先單獨學習下 LiveData 的使用。
LiveData 的使用分三步:
observe( LifecycleOwner owner, Observer<? super T> observer)
,第一個參數是 LifecycleOwner對象,這也是 LiveData 能監聽生命週期的能力來源。第二個參數就是咱們的監聽器對象 Observer 。添加 LiveData 和 ViewModel 的依賴:
implementation "android.arch.lifecycle:extensions:1.1.1"
固然,你也能夠分別單獨集成 LiveData 和 ViewModel:
implementation "android.arch.lifecycle:livedata:1.1.1"
implementation "android.arch.lifecycle:viewmodel:1.1.1"
接下來就對照上面講的三步走戰略,建立以下代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; private MutableLiveData<Integer> mNumberLiveData; private TextView mTvNumber; private Button mBtnStart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTvNumber = findViewById(R.id.tv_number); mBtnStart = findViewById(R.id.btn_start); mBtnStart.setOnClickListener(this); mNumberLiveData = new MutableLiveData<>(); mNumberLiveData.observe(this, new Observer<Integer>() { @Override public void onChanged(@Nullable Integer integer) { mTvNumber.setText("" + integer); Log.d(TAG, "onChanged: " + integer); } }); } @Override public void onClick(View v) { new Thread() { @Override public void run() { super.run(); int number = 0; while (number < 5) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } number++; mNumberLiveData.postValue(number); } } }.start(); } }
這裏,咱們在 onCreate 方法中建立了一個 MutableLiveData 類型的變量 mNumberLiveData ,並將其泛型指定爲 Integer,經過其observe(...)
方法把 this 傳進去(this爲 AppCompatActivity,實現了 LifecycleOwner 接口,支持包爲 28.0.0),並傳進去一個 Observer,在其onChanged(...)
方法中,咱們將變化後的數據 integer 設置給 TextView 顯示。爲了便於觀察,咱們同時在控制檯打印一行對應的日誌。
Demo 的界面很簡單,就是一個按鈕,一個 TextView ,點擊按鈕,開啓一個子線程,每過3秒經過postValue(...)
修改 LiveData 中的值(若是是在UI線程,能夠直接經過 setValue(...)
來修改)。
這裏咱們點擊開始,並在數字還沒變爲 5 的時候,就按Home鍵
進入後臺,等過一段時間以後,在進入頁面,會發現頁面最終顯示爲數字 「5」,可是打印的結果並非連續的1~5,而是有中斷:
這也證實了當程序進入後臺,變爲 inactive 狀態時,並不會收到數據更新的通知,而是在從新變爲 active 狀態的時候纔會收到通知,並執行onChanged(...)
方法。
上面能夠看到,咱們使用 LiveData 的時候,實際使用的是它的子類 MutableLiveData,LiveData 是一個接口,它並無給咱們暴露出來方法供咱們對數據進行修改。若是咱們須要對數據修改的時候,須要使用它的具體實現類 MutableLiveData,其實該類也只是簡單的將 LiveData 的 postValue(...)
和 setValue(...)
暴露了出來:
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); } }
MutableLiveData<T>
實際上是對數據進行了一層包裹。在它的泛型中能夠指定咱們的數據類。能夠存儲任何數據,包括實現了 Collections 接口的類,好比 List 。
有時候咱們須要在 observer 的 lifecycle 處於 active 狀態時作一些操做,那麼咱們就能夠經過繼承 LiveData 或者 MutableLiveData,而後覆寫其onActive()
和onInactive()
方法。這兩個方法的默認實現均爲空。像下面這樣:
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); } }
LiveData 具備生命週期感知能力,能在 Activity 銷燬的時候自動取消監聽,這也意味着它能夠用來在多個 Activity 間共享數據。咱們能夠藉助單例來實現,這裏直接飲用官方 Demo :
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); } }
有時候,咱們須要在將 LiveData 中存儲的數據分發給 Observer 以前進行一些修改。好比咱們例子中拿到的是 Integer 類型的返回值,咱們設置進 TextView 的時候,直接使用mTvNumber.setText(integer)
會報錯,須要使用mTvNumber.setText("" + integer)
這種形式,但我想在這裏直接拿到已經處理過的 String 數據,拿到就能直接用,而不須要再在這裏手動拼。咱們能夠經過Transformations
類的 map
操做符來實現這個功能。
原始的代碼爲:
mNumberLiveData = new MutableLiveData<>(); mNumberLiveData.observe(this, new Observer<Integer>() { @Override public void onChanged(@Nullable Integer integer) { mTvNumber.setText("" + integer); Log.d(TAG, "onChanged: " + integer); } });
使用 Transformations.map(...)
改造以後的代碼:
mNumberLiveData = new MutableLiveData<Integer>(); Transformations.map(mNumberLiveData, new Function<Integer, String>() { @Override public String apply(Integer integer) { return "" + integer; } }).observe(this, new Observer<String>() { @Override public void onChanged(@Nullable String s) { mTvNumber.setText(s); Log.d(TAG, "onChanged: " + s); } });
這就實現了將一種類型的數據轉化爲另外一種類型的數據。map 操做符會返回一個改造以後的 LiveData,直接對這個 LiveData 進行監聽便可。這裏的map
操做符相似於 RxJava 的map
。
但有時候咱們並不僅是須要簡單的把數據由一種類型轉爲另外一種類型。咱們可能須要的更高級一點。
好比,咱們一方面須要一個存儲 userId 的 LiveData,另外一方面又須要維護一個存儲 User 信息的 LiveData,然後者的 User 則是根據 userId 來從數據庫中查找的,兩者須要對應。這時候咱們就可使用Transformations
類的switchMap(...)
操做符。
MutableLiveData<String> userIdLiveData = new MutableLiveData<>(); LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, new Function<String, LiveData<User>>() { @Override public LiveData<User> apply(String userId) { // 根據 userId 返回一個 LiveData<User>,能夠經過Room來獲取 return getUser(userId); } });
這裏,咱們在覆寫的apply(...)
方法中,每次 userId 發生變化以後,會自動經過 getUser(userId) 去獲取一個封裝有 User 對象的 LiveData。若是是從數據庫獲取的話,使用 Google 推出的配套的數據庫組件 Room 會比較爽,由於它能直接返回一個 LiveData。關於 Room,有時間的話以後再寫文章講解。
從上面能夠看出,LiveData 包中提供的 Transformations 很是有用,能讓咱們的整個調用過程變成鏈式。但 Transformations 只提供了map(...)
和switchMap(...)
兩個方法,若是咱們有其餘更復雜的需求,就須要本身經過MediatorLiveData
類來建立本身的transformations
。話說回來,其實上面兩個方法的內部,就是經過MediatorLiveData
來實現的,經過 MediatorLiveData 進行了一次轉發。這裏貼出Transformations
的源碼:
public class Transformations { private Transformations() { } @MainThread public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source, @NonNull final Function<X, Y> func) { final MediatorLiveData<Y> result = new MediatorLiveData<>(); result.addSource(source, new Observer<X>() { @Override public void onChanged(@Nullable X x) { result.setValue(func.apply(x)); } }); return result; } @MainThread public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger, @NonNull final Function<X, LiveData<Y>> func) { final MediatorLiveData<Y> result = new MediatorLiveData<>(); result.addSource(trigger, new Observer<X>() { LiveData<Y> mSource; @Override public void onChanged(@Nullable X x) { LiveData<Y> newLiveData = func.apply(x); if (mSource == newLiveData) { return; } if (mSource != null) { result.removeSource(mSource); } mSource = newLiveData; if (mSource != null) { result.addSource(mSource, new Observer<Y>() { @Override public void onChanged(@Nullable Y y) { result.setValue(y); } }); } } }); return result; } }
源碼比較簡單,再也不詳細講解。
它裏面其實主要用的就是MediatorLiveData
,經過該類咱們能組合多個 LiveData 源。當任何一個 LiveData 源發生改變的時候,MediatorLiveData
的 Observers 都會被觸發,這點比較實用。好比咱們有兩個 LiveData,一個是從數據庫獲取,一個是從網絡獲取。經過MediatorLiveData
就能作到,當兩者任何一個獲取到最新數據,就去觸發咱們的監聽。
順便也貼下MediatorLiveData
的源碼,它繼承自MutableLiveData
:
public class MediatorLiveData<T> extends MutableLiveData<T> { private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>(); @MainThread public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<S> onChanged) { Source<S> e = new Source<>(source, onChanged); Source<?> existing = mSources.putIfAbsent(source, e); if (existing != null && existing.mObserver != onChanged) { throw new IllegalArgumentException( "This source was already added with the different observer"); } if (existing != null) { return; } if (hasActiveObservers()) { e.plug(); } } @MainThread public <S> void removeSource(@NonNull LiveData<S> toRemote) { Source<?> source = mSources.remove(toRemote); if (source != null) { source.unplug(); } } @CallSuper @Override protected void onActive() { for (Map.Entry<LiveData<?>, Source<?>> source : mSources) { source.getValue().plug(); } } @CallSuper @Override protected void onInactive() { for (Map.Entry<LiveData<?>, Source<?>> source : mSources) { source.getValue().unplug(); } } private static class Source<V> implements Observer<V> { final LiveData<V> mLiveData; final Observer<V> mObserver; int mVersion = START_VERSION; Source(LiveData<V> liveData, final Observer<V> observer) { mLiveData = liveData; mObserver = observer; } void plug() { mLiveData.observeForever(this); } void unplug() { mLiveData.removeObserver(this); } @Override public void onChanged(@Nullable V v) { if (mVersion != mLiveData.getVersion()) { mVersion = mLiveData.getVersion(); mObserver.onChanged(v); } } } }
這裏順便提一句,若是想在數據更新的時候讓 Observer當即獲得通知,也就是說忽略生命週期狀態,這時候咱們可使用 LiveData 的observeForever(Observer<T> observer)
方法。
LiveData 每每是須要結合 ViewModel才能發揮出更大的威力。下面就接着介紹 ViewModel 的知識,以及兩者的搭配使用。
簡單來說,ViewModel 是一種用來存儲和管理UI相關數據的類。但不一樣的是,它支持在系統配置發生改變的時候自動對數據進行保存。固然,這要配合 LiveData。
咱們知道,在屏幕旋轉的時候,會致使Activity/Fragment重繪,會致使咱們以前的數據丟失。就好比,若是咱們使用EditText,在裏面輸入了內容,可是屏幕旋轉的時候,會發現其中的text內容被清空了。若是你發現沒清空,可能使用的是 support 包下的控件,或者 Activity 繼承自 AppCompatActivity,而且給該控件添加了 id。系統對一些簡單的數據進行了恢復(實際上是在EditText的父類TextView進行的恢復)。
對於一些簡單的數據,咱們能夠經過在Activity的 onSaveInstanceState()
方法中存儲,而後在onCreate()
中進行恢復,可是這種方式只適合存儲少許的數據,而且是能被序列化和反序列化的數據。而對那些大量的數據則不適用,好比一個 User 或者 Bitmap 的 List。
此外,它也使得 View 的數據持有者和 UI controller 邏輯更加分離,便於解耦和測試。
以前咱們是單獨使用 LiveData,這裏配合ViewModel使用:
public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<User>>(); loadUsers(); } return users; } private void loadUsers() { // Do an asynchronous operation to fetch users. } }
能夠看到,這裏咱們建立一個類,繼承自ViewModel,而後在裏面存儲咱們須要的MutableLiveData
字段。注意,getUsers()方法返回的類型是LiveData而非 MutableLiveData,由於咱們通常不但願在ViewModel 外面對數據進行修改,因此返回的是一個不可變的 LiveData 引用。若是想對數據進行更改,咱們能夠暴露出來一個setter
方法。
接下來能夠按照以下的方式獲取 ViewModel:
public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class); model.getUsers().observe(this, users -> { // update UI }); } }
咱們在onCreate()方法中經過ViewModelProviders.of(this).get(MyViewModel.class);
這行代碼來獲取一個MyViewModel
實例。以後又經過該實例暴露出來的getter方法獲取LiveData 實例。這裏要注意,當Activity重建的時候,雖然 onCreate() 方法會從新走一遍,可是這個MyViewModel
實例,仍然是第一次建立的那個實例,在ViewModelProviders.of(this).get(***.class)
中的get
方法中進行了緩存。以後進行源碼解析的時候會詳細講解。先看下下面的一張圖,瞭解下 ViewModel 的整個生命週期:
ViewModel 最終消亡是在 Activity 被銷燬的時候,會執行它的onCleared()
進行數據的清理。
Fragment 間共享數據比較常見。一種典型的例子是屏幕左側是一個 Fragment,其中存儲了一個新聞標題列表,咱們點擊一個 item,在右側的 Fragment 中顯示該新聞的詳細內容。這種場景在美團等訂餐軟件中也很常見。
經過 ViewModel 將使得數據在各 Fragment 之間的共享變得更加簡單。
咱們須要作的僅僅是在各 Fragment 的 onCreate() 方法中經過:
ViewModelProviders.of(getActivity()).get(***ViewModel.class);
來獲取 ViewModel ,注意,of(...)
方法中傳入的是兩者所在的activity
。具體能夠參考以下官方代碼:
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData<Item> getSelected() { return selected; } } public class MasterFragment extends Fragment { private SharedViewModel model; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 傳入 activity model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 傳入 activity SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); model.getSelected().observe(this, { item -> // Update the UI. }); } }
Android 3.0 中引入了 Loader 機制,讓開發者能輕鬆在 Activity 和 Fragment 中異步加載數據。但事實上用的人並很少。如今,它幾乎能夠退出歷史舞臺了。ViewModel配合Room數據庫以及LiveData,徹底能夠替代Loader,在SDK28裏,也愈來愈多的用Loader也愈來愈多的被替代。
但要注意,ViewModel能用來替換Loader,可是它卻並非設計用來替換onSaveInstanceState(...)
的。關於數據持久化以及恢復UI狀態等,能夠參考下Medium上的這篇文章,講的簡直不能再好了:ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
一般 LiveData 是須要配合 ViewModel 使用的。ViewModel 負責在系統配置更改時保存和恢復 LiveData,而 LiveData 則負責在生命週期狀態發生改變的時候,對數據的變化進行監聽。
寫到這裏算是把 LiveData 和 ViewModel 的使用講完了。這裏我在開篇故意單獨把 LiveData 和 ViewModel 分開講解,相比較官網更加容易理解。但若是想對兩者進行詳細瞭解,仍是建議把官方文檔認真的多閱讀幾遍。
歡迎關注公衆號來獲取最新消息。