初到掘金,人生地不熟,喜歡的朋友,點個贊鼓勵下新手唄~
java
參考連接:
https://developer.android.google.cn/topic/libraries/architecture/livedata https://mp.weixin.qq.com/s/ir3DBkGt5mna3RDjTpRFOQ
LiveData是google發佈的lifecycle-aware components中的一個組件,除了能實現數據和View的綁定響應以外,它最大的特色就是具有生命週期感知功能
android
LiveData採用了觀察者模式,當數據發生變化時,主動通知被觀察者 。
架構
因爲LiveData會在Activity/Fragment等具備生命週期的lifecycleOwner組件調用onDestory的時候自動解綁,因此解決了可能存在的內存泄漏問題。以前咱們爲了不這個問題,通常有註冊綁定的地方都要解綁(即註冊跟解綁要成對出現),而LiveData利用生命週期感知功能解決了這一問題,能夠實現只需關心註冊,而解綁會根據生命週期自動進行的功能。
app
當Activity組件處於inactive非活動狀態時,它不會收到LiveData數據變化的通知。
less
觀察者並不須要手動處理生命週期變化對自身的邏輯的影響,只須要關心如何處理獲取到的數據。LiveData可以感知Activity/Fragment等組件的生命週期變化,因此就徹底不須要在代碼中告訴LiveData組件的生命週期狀態,當數據發生變化時,只在生命週期處於active下通知觀察者,而在inactive下,不會通知觀察者。
異步
什麼意思呢?第一種狀況,當觀察者處於active活動狀態。LiveData基於觀察者模式,因此當數據發生變化,觀察者可以立刻獲取到最新變化;第二種狀況,當觀察者處於inactive非活動狀態。LiveData只能生命週期active下發送數據給觀察者。舉個例子,當Activity處於後臺(inactive)時,LiveData接收到了新的數據,但這時候LiveData並不會通知該Activity,可是當該Activity從新返回前臺(active)時會繼續接收到最新的數據。一句話歸納,LiveData是粘性的。ide
咱們知道,當你把數據存儲在組件中時,當configuration change(好比語言、屏幕方向變化)時,組件會被recreate,然而系統並不能保證你的數據可以被恢復的。當咱們採用LiveData保存數據時,由於數據和組件分離了。當組件被recreate,數據仍是存在LiveData中,並不會被銷燬。
源碼分析
經過繼承LiveData類,而後將該類定義成單例模式,在該類封裝監聽一些系統屬性變化,而後通知LiveData的觀察者。
post
LiveData註冊的觀察者的兩種方式:優化
// 這個方法添加的observer會受到owner生命週期的影響,在owner處於active狀態時,有數據變化,會通知,
// 當owner處於inactive狀態,不會通知,而且當owner的生命週期狀態時DESTROYED時,自動removeObserver
public void observe (LifecycleOwner owner, Observer<? super T> observer) // 這個方法添加的observer不存在生命週期概念,只要有數據變化,LiveData都會通知,而且不會自動remove public void observeForever (Observer<? super T> observer) 複製代碼
LiveData發佈修改的兩種方式:
// 這個方法必須在主線程調用
protected void setValue (T value) // 這個方式主要用於在非主線程調用 protected void postValue (T value) 複製代碼
LiveData 官方API文檔: https://developer.android.google.cn/reference/androidx/lifecycle/LiveData
舉個例子:在Activity頁面有一TextView,須要展現用戶User的信息,User 類定義:
public class User {
public String name;
public int sex;
public User(String name, int sex) {
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
", name='" + name + '\'' +
", sex='" + sex+ '\'' +
'}';
}
}複製代碼
常規的作法:
// 獲取User的數據後
mTvUser.setText(user.toString());複製代碼
這樣作的一個問題,若是獲取或者修改User的來源不止一處,那麼須要在多個地方更新TextView,而且若是在多處UI用到了User,那麼也須要在多處更新。
怎麼優化這個問題呢?使用 LiveData。不少時候,LiveData與ViewModel組合使用(ViewModel後續文章會分析),讓LiveData持有User 實體,做爲一個被觀察者,當User改變時,通知全部使用User的觀察者自動change。
首先構建一個UserViewModel以下:
public class UserViewModel extends ViewModel {
//聲明userLiveData
private MutableLiveData<User> userLiveData;
//獲取userLiveData
public LiveData getUserLiveData() {
if(userLiveData == null) {
userLiveData = new MutableLiveData<>();
}
return userLiveData;
}
}複製代碼
而後註冊觀察者:
public class TestActivity extends AppCompatActivity {
private UserViewModel mModel;
private TextView mUserTextView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
mModel = ViewModelProviders.of(this).get(UserViewModel.class);
//建立用來更新ui的觀察者,重寫onChange方法,該方法會在數據發生變化時
//經過LiveData調用觀察者的onChange對數據變化響應
final Observer<User> userObserver = new Observer<User>() {
@Override
public void onChanged(@Nullable User user) {
LogUtil.i(TAG, user.toString());
//當收到LiveData發來的新數據時,更新
mUserTextView.setText(user.toString());
}
};
//獲取監聽User數據的LiveData
LiveData userLiveData = mModel.getUserLiveData();
//註冊User數據觀察者
userLiveData.observe(this, userObserver);
}
}複製代碼
數據源發送改變的時候:
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
User user = new User("zhang san", 1)
//調用LiveData的setValue方法通知觀察者數據變化
mModel.getUserLiveData().setValue(user);
}
});複製代碼
這樣使用到 User 的地方,UI 會自動更新,日誌以下:
com.example.cimua I/TestActivity : User{name='zhang san', sex=1}複製代碼
好了,LiveData的基本使用就是這麼簡單~~~
LiveData更多用法能夠看官方文檔 https://developer.android.google.cn/topic/libraries/architecture/livedata
LiveData是基於觀察者模式構建的,因此,咱們分析LiveData的源碼主要能夠分紅兩部分:
如今咱們先看看observe()方法,由於observeForever()方法的實現跟observe()是相似的,咱們就不看了,這裏只看observe():
// 咱們已經知道,LiveData可以感知生命週期的變化。從傳入的第一個參數是LifecycleOwner類型,咱們已經
// 能夠知道,原來LiveData是基於Lifecycle架構的基礎上擴展的,咱們在前一篇Lifecyle文章中已經分析過
// Lifecyle組件了,LiveData就是封裝了特定的LifecycleObserver並將其註冊到LifecycleOwner中,用以感知
// 生命週期
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
// 若是LifecycleOwner的生命週期狀態已是DESTROYED,例如Activity已經destroy,
// 那麼就不必添加觀察者,直接返回就能夠了
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 建立繼承了GenericLifecycleObserver的LifecycleBoundObserver,而且將這個LifecycleBoundObserver
// 存進觀察者集合mObservers中統一管理
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
// 一個observer對象,只能監聽一個LifecycleOwner的生命週期,
// 若是試圖監聽不一樣LifecycleOwner的生命週期,直接拋異常
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 調用observe()這個方法添加的observer,只有Activity/Fragment等生命週期組件可見時
// 纔會收到數據更新的通知,爲了知道何時Activity/Fragment是可見的,這裏須要註冊到
// Lifecycle中感知生命週期
// 也是由於這個,observe()比observeForever()多了一個參數lifecycleOwner
owner.getLifecycle().addObserver(wrapper);
}複製代碼
接着咱們在看看observe()方法中最重要的LifecycleBoundObserver:
private abstract class ObserverWrapper {
final Observer<T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
// LiveData.this.mActiveCount表示處於active狀態的observer的數量
// 當mActiveCount大於0時,LiveData處於active狀態
// 注意區分observer的active狀態和 LiveData 的active狀態
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
// inactive -> active
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
// mActiveCount在咱們修改前等於1,也就是說,LiveData從active
// 狀態變到了inactive
onInactive();
}
//若是是active狀態,通知觀察者數據變化(dispatchingValue方法在下一節發佈中分析)
if (mActive) {
// observer從inactive到active,此時客戶拿到的數據可能不是最新的,這裏須要dispatch
// 關於他的實現,咱們下一節再看
dispatchingValue(this);
}
}
}
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@NonNull final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
super(observer);
mOwner = owner;
}
// 這個方法返回的是當前是不是active狀態
@Override
boolean shouldBeActive() {
// 只有當生命週期狀態是STARTED或者RESUMED時返回true,其餘狀況返回false
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
// 這個方法是由Lifecycle結構中的mLifecycleRegistry所調用,一旦LifecycleOwner的生命週期
// 發生變化,都會調用到onStateChanged這個方法進行生命週期轉換的通知
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// 上面咱們說過,使用LiveData只須要關心註冊,不須要關心什麼時候解綁,這裏就告訴咱們答案:
// 當生命週期狀態爲DESTROYED,會自動removeObserver實現解綁,不會致使內存泄露。
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// 一開始建立LifecycleBoundObserver實例的時候,mActive默認爲false,
// 當註冊到Lifecycle後,Lifecycle會同步生命週期狀態給咱們(也就是回調本方法),
// 不熟悉lifecycle的讀者,能夠看前一篇講述Lifecycle的文章
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}複製代碼
到這裏,LiveData的訂閱流程就基本分析完了。
上面咱們已經說了,LiveData發佈修改有setValue已經postValue兩種方式,其中setValue只能在主線程調用,postValue則沒有這個限制。
咱們從setValue的分析入手發佈流程:
@MainThread
protected void setValue(T value) {
// 判斷當前調用線程是不是主線程,若是不是,直接拋IllegalStateException異常
assertMainThread("setValue");
// 每次更新value,都會使mVersion + 1,ObserverWrapper也有一個字段,叫mLastVersion
// 經過比較這兩個字段,能夠避免重複通知觀察者,還能夠用於實現LiveData的粘性事件特性(後面會說到)
mVersion++;
// 將此次數據保存在LiveData的mData變量中,mData的值永遠是最新的值
mData = value;
// 發佈
dispatchingValue(null);
}複製代碼
接着再看看dispatchingValue方法:
// 若是參數initiator爲null的話,表示要將新數據發佈通知給全部observer
// 若是參數不爲null的話,表示只通知給傳入的觀察者initiator
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
// 在observer的回調裏面又觸發了數據的修改
// 設置mDispatchInvalidated爲true後,可讓下面的循環知道
// 數據被修改了,從而開始一輪新的迭代。
//
// 比方說,dispatchingValue -> observer.onChanged -> setValue -> dispatchingValue
// 這裏return的是後面那個dispatchingValue,而後在第一個
// dispatchingValue會從新遍歷全部的observer,並調用他們的onChanged。
//
// 若是想避免這種狀況,能夠在回調裏面使用 postValue 來更新數據
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
// 調用 observer.onChanged()
considerNotify(initiator);
initiator = null;
} else {
// initiator不爲空,遍歷mObservers集合,試圖通知全部觀察者
for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
// 某個客戶在回調裏面更新了數據,break後,這個for循環會從新開始
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}複製代碼
看過我那篇 lifecycle 源碼分析的讀者應該對 dispatchingValue 處理循環調用的方式很熟悉了。以這裏爲例,爲了防止循環調用,咱們在調用客戶代碼前先置位一個標誌(mDispatchingValue),結束後再設爲 false。若是在回調裏面又觸發了這個方法,能夠經過 mDispatchingValue 來檢測。
檢測到循環調用後,再設置第二個標誌(mDispatchInvalidated),而後返回。返回又會回到以前的調用,前一個調用經過檢查 mDispatchInvalidated,知道數據被修改,因而開始一輪新的迭代。
接着繼續看considerNotify方法:
private void considerNotify(ObserverWrapper observer) {
// 若是observer的狀態不是active,那麼不向該observer通知,直接返回
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// 上面的源碼分析,咱們知道每一次setValue或者postValue的調用都會是mVersion自增1,
// mLastVersion的做用是爲了與mVersion做比較,這個比較做用主要有兩點:
// 1.若是說mLastVersion >= mVersion,證實這個觀察者已經接受過本次發佈事件通知,不須要重複通知了,直接返回
// 2.實現粘性事件。好比有一個數據(LiveData)在A頁面setValue()以後,則該數據(LiveData)中的
// 全局mVersion+1,也就標誌着數據版本改變,而後再從A頁面打開B頁面,在B頁面中開始訂閱該LiveData,
// 因爲剛訂閱的時候內部的數據版本都是從-1開始,此時內部的數據版本就和該LiveData全局的數據
// 版本mVersion不一致,根據上面的原理圖,B頁面打開的時候生命週期方法一執行,則會進行notify,
// 此時又同時知足頁面是從不可見變爲可見、數據版本不一致等條件,因此一進B頁面,B頁面的訂閱就會被響應一次
if (observer.mLastVersion >= mVersion) {
return;
}
// 設置mLastVersion = mVersion,以避免重複通知觀察者
observer.mLastVersion = mVersion;
// 這裏就最終調用了咱們一開始經過observe()方法傳入的observer中的onChange()方法
// 即新數據被髮布給了observer
observer.mObserver.onChanged((T) mData);
}複製代碼
setValue的分析就到此爲止了~
在setValue的基礎上,分析postValue就比較簡單了:
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 經過handler將mPostValueRunnable分發到主線程執行,其實最終執行的也是setValue方法
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}複製代碼
LiveData的源碼就分析這麼多了,更詳細就須要到android源碼裏面找了。
將LiveData的源碼抽象爲一張流程圖來展現,下面的其餘問題均可以在這張圖中找到答案:
lifecycle-aware compents的核心就是生命週期感知,要明白LiveData爲何能感知生命週期,就要知道Google的這套生命週期感知背後的原理是什麼,下面是我基於以前lifeycycle這套東西剛出來時候對源碼進行的一個分析總結(如今的最新代碼可能和以前有點出入,可是原理上基本是同樣的):
首先Activity/Fragment是LifecycleOwner(26.1.0以上的support包中Activity已經默認實現了LifecycleOwner接口),內部都會有一個LifecycleRegistry存放生命週期State、Event等。而真正核心的操做是,每一個Activity/Fragment在啓動時都會自動添加進來一個Headless Fragment(無界面的Fragment),因爲添加進來的Fragment與Activity的生命週期是同步的,因此當Activity執行相應生命週期方法的時候,同步的也會執行Headless Fragment的生命週期方法,因爲這個這個Headless Fragment對咱們開發者來講是隱藏的,它會在執行本身生命週期方法的時候更新Activity的LifecycleRegistry裏的生命週期State、Event, 而且notifyStateChanged來通知監聽Activity生命週期的觀察者。這樣就到達了生命週期感知的功能,因此實際上是一個隱藏的Headless Fragment來實現了監聽者能感知到Activity的生命週期。
基於這套原理,只要LiveData註冊了對Activity/Fragment的生命週期監聽,也就擁有了感知生命週期的能力。
因爲LiveData會在Activity/Fragment等具備生命週期的lifecycleOwner onDestory的時候自動解綁,因此解決了可能存在的內存泄漏問題。以前咱們爲了不這個問題,通常有註冊綁定的地方都要解綁,而LiveData利用生命週期感知功能解決了這一問題。
咱們能夠知道,當Activity/Fragment的生命週期發生改變時,LiveData中的監聽都會被回調,因此避免內存泄漏就變得十分簡單,能夠看上圖,當LiveData監聽到Activity onDestory時則removeObserve,使本身與觀察者自動解綁。這樣就避免了內存泄漏。
咱們一般在一個異步任務回來後須要更新View,而此時頁面可能已經被回收,致使常常會出現View空異常,而LiveData因爲具有生命週期感知功能,在界面可見的時候纔會進行響應,如界面更新等,若是在界面不可見的時候發起notify,會等到界面可見的時候才進行響應更新。因此就很好的解決了空異常的問題。
所謂粘性,也就是說消息在訂閱以前發佈了,訂閱以後依然能夠接受到這個消息,像EventBus實現粘性的原理是,把發佈的粘性事件暫時存在全局的集合裏,以後當發生訂閱的那一刻,遍歷集合,將事件拿出來執行。
而LiveData之因此自己就是粘性的,結合上面的原理圖咱們來分析一下,好比有一個數據(LiveData)在A頁面setValue()以後,則該數據(LiveData)中的全局mVersion+1,也就標誌着數據版本改變,而後再從A頁面打開B頁面,在B頁面中開始訂閱該LiveData,因爲剛訂閱的時候內部的數據版本都是從-1開始,此時內部的數據版本就和該LiveData全局的數據版本mVersion不一致,根據上面的原理圖,B頁面打開的時候生命週期方法一執行,則會進行notify,此時又同時知足頁面是從不可見變爲可見、數據版本不一致等條件,因此一進B頁面,B頁面的訂閱就會被響應一次。這就是所謂的粘性,A頁面在發消息的時候B頁面是還沒建立還沒訂閱該數據的,可是一進入B頁面一訂閱,以前在A中發的消息就會被響應。
初到掘金,人生地不熟,喜歡的朋友,點個贊鼓勵下新手唄~