Android 官方架構組件(二)——LiveData

初到掘金,人生地不熟,喜歡的朋友,點個贊鼓勵下新手唄~
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的優勢

  • 可以確保數據和UI統一

LiveData採用了觀察者模式,當數據發生變化時,主動通知被觀察者 。
架構

  • 解決內存泄露問題

因爲LiveData會在Activity/Fragment等具備生命週期的lifecycleOwner組件調用onDestory的時候自動解綁,因此解決了可能存在的內存泄漏問題。以前咱們爲了不這個問題,通常有註冊綁定的地方都要解綁(即註冊跟解綁要成對出現),而LiveData利用生命週期感知功能解決了這一問題,能夠實現只需關心註冊,而解綁會根據生命週期自動進行的功能。
app

  • 當Activity中止時不會引發崩潰

當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 changes時,不須要額外的處理來保存數據

咱們知道,當你把數據存儲在組件中時,當configuration change(好比語言、屏幕方向變化)時,組件會被recreate,然而系統並不能保證你的數據可以被恢復的。當咱們採用LiveData保存數據時,由於數據和組件分離了。當組件被recreate,數據仍是存在LiveData中,並不會被銷燬。
源碼分析

  • 資源共享

經過繼承LiveData類,而後將該類定義成單例模式,在該類封裝監聽一些系統屬性變化,而後通知LiveData的觀察者。
post


LiveData的簡單使用

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基本使用步驟總結:

  1. 建立LiveData
  2. 建立觀察者Observer
  3. 調用LiveData的observe方法將LiveData以及Observer創建起發佈-訂閱關係
  4. 在適當的時機調用LiveData的setValue或者postValue發佈新數據通知觀察者


LiveData源碼分析

LiveData是基於觀察者模式構建的,因此,咱們分析LiveData的源碼主要能夠分紅兩部分:

  • 訂閱:即調用LiveData的observe方法註冊
  • 發佈:即調用LiveData的setValue或者postValue方法發佈數據

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的發佈流程分析

上面咱們已經說了,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的源碼抽象爲一張流程圖來展現,下面的其餘問題均可以在這張圖中找到答案:

  • 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爲何能夠避免內存泄露?

因爲LiveData會在Activity/Fragment等具備生命週期的lifecycleOwner onDestory的時候自動解綁,因此解決了可能存在的內存泄漏問題。以前咱們爲了不這個問題,通常有註冊綁定的地方都要解綁,而LiveData利用生命週期感知功能解決了這一問題。

咱們能夠知道,當Activity/Fragment的生命週期發生改變時,LiveData中的監聽都會被回調,因此避免內存泄漏就變得十分簡單,能夠看上圖,當LiveData監聽到Activity onDestory時則removeObserve,使本身與觀察者自動解綁。這樣就避免了內存泄漏。 

  • LiveData爲何能夠解決空指針異常?

咱們一般在一個異步任務回來後須要更新View,而此時頁面可能已經被回收,致使常常會出現View空異常,而LiveData因爲具有生命週期感知功能,在界面可見的時候纔會進行響應,如界面更新等,若是在界面不可見的時候發起notify,會等到界面可見的時候才進行響應更新。因此就很好的解決了空異常的問題。

  • LiveData爲何是粘性的?

所謂粘性,也就是說消息在訂閱以前發佈了,訂閱以後依然能夠接受到這個消息,像EventBus實現粘性的原理是,把發佈的粘性事件暫時存在全局的集合裏,以後當發生訂閱的那一刻,遍歷集合,將事件拿出來執行。

而LiveData之因此自己就是粘性的,結合上面的原理圖咱們來分析一下,好比有一個數據(LiveData)在A頁面setValue()以後,則該數據(LiveData)中的全局mVersion+1,也就標誌着數據版本改變,而後再從A頁面打開B頁面,在B頁面中開始訂閱該LiveData,因爲剛訂閱的時候內部的數據版本都是從-1開始,此時內部的數據版本就和該LiveData全局的數據版本mVersion不一致,根據上面的原理圖,B頁面打開的時候生命週期方法一執行,則會進行notify,此時又同時知足頁面是從不可見變爲可見、數據版本不一致等條件,因此一進B頁面,B頁面的訂閱就會被響應一次。這就是所謂的粘性,A頁面在發消息的時候B頁面是還沒建立還沒訂閱該數據的,可是一進入B頁面一訂閱,以前在A中發的消息就會被響應。

初到掘金,人生地不熟,喜歡的朋友,點個贊鼓勵下新手唄~
相關文章
相關標籤/搜索