LiveData作爲jettpack的一環,常搭配ViewModel一起使用;相比於RxJava,用LiveData封裝的LiveDataBus更加簡潔方便,並且LiveData會感知頁面的信息變化,當頁面是不可見的時候,及時Data有數據更新,頁面也不會接收到,然後在頁面重新可見再去把數據通知給頁面,而且在頁面銷燬時也會及時銷燬數據,大大降低了內存泄露的發生;這篇主要簡單分析一下LiveData的流程
1.LiveData的使用
class TestActivity :AppCompactActivity(){
var statusData: MutableLiveData<TestLiveStatus> = MutableLiveData()
override fun onCreate(savedInstanceState: Bundle?) {
...
statusData.observe(this, this)
}
override fun onChanged(t: TestLiveStatus?) {
Log.e("TAG", "onChanged -> $t ");
}
}
使用很方便,只需要註冊一個監聽,然後在監聽裏處理狀態變化就可以
2.LiveData源碼分析
2.1 livedata的註冊
@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);
}
LifeCycle是頁面的生命週期監聽,主要是通過在Activity裏面添加一個空的ReportFragment,監聽這個Fragment的狀態來實現;具體我會在下一篇中描述,這裏只需要知道這個可以感知Activity的生命週期即可
這裏先會創建一個LifeCycleBoundObserver ,並把owner和observer都傳進去,然後用mObserv
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);
}
這裏主要關注兩個方法shouldBeActive和onStateChanged
shouldBeActivite個枚舉判斷,如果枚舉的類型比STARTED大則返回true
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
我們看一下這個State的聲明
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
比STARTED大的只有RESUME
LifeCycle和這個State的有個對應關係可以參考一下,所以我們可以認爲這個shouldBeActive返回的是表示是頁面是否是從不可見變成了可見狀態
static State getStateAfter(Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException("Unexpected event value " + event);
}
onStateChanged中包含兩部分
1.當頁面被銷燬時,註冊的觀察者被清除,後續的事件通知也隨之終止
2.當頁面非銷燬狀態時,處理相應的狀態變化邏輯
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
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);
}
}
可以看出,這個方法只處理活躍狀態變化的,而從上面的分析,可以認爲就是頁面可見性變化的處理
默認mActiveCount = 0 , 每次從不可見變成可見+1,而從可見變成不可見-1
onActive和onInactive則分別是處理這兩種狀態的方法,這裏這兩個都是空實現
最後的當變成可見後 會調用 dispatchingValue 通知消息更新
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;
}
這裏雖然有改while循環,但大部分情況下只會執行一次,因爲mDispatchInvalidated一進來就會設置成false ; 對於併發情況會執行多次,因爲在入口處併發會重新設置成true
有兩種情況,initiator爲空和不爲空,爲空是通知所有的觀察者,不爲空則是通知傳入的initiator;
主要差異是主動和被動
爲null主要場景是setValue等主動設置值的情況,會同步更新所有註冊的監聽
不爲null的主要場景是狀態變更,每個observer自己去刷新數據
然後我們看一下considerNotify方法
private void considerNotify(ObserverWrapper 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;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
這裏會先判斷是否是活躍的,不活躍的直接返回
再判定狀態是否resume的,不是的話就回到上面的狀態變更方法;這裏相當於做了活躍的二次判斷
然後判斷mLastVersion和mVersion的大小,只有mVersion比mLastVersion大才會去通知observer去觀察數據,同時把mLastVersion賦值成新的值;這個主要是避免重新分發,如果沒這個判斷,則每次從不可見變成可見都會分發一次最新的消息,這肯定不是我們所想要的結果
然後設置值的方法
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
在這裏會把mVersion++ , 並調用上面的dispatchingValue(null)方法通知所有的observer
注意的是
static final int START_VERSION = -1;
private int mVersion = START_VERSION;
private abstract class ObserverWrapper {
int mLastVersion = START_VERSION;
...
}
初始值mVersion和mLastVersion都是 -1 ,而LiveData有兩個構造方法區別一下
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
/**
* Creates a LiveData with no value assigned to it.
*/
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
無參的構造方法mVersion=-1,而傳一個data數據的mVersion=0
就是說,有數據的構造方法,初始值判斷 mVersion> mLastVersion是成立的,而無參的是不成立的
此外,因爲setValue會使mVersion++, 如果在註冊觀察者之前就調用了setValue方法,那麼在註冊後,頁面變成可見的情況下,會強制把之前set的值刷新到方法回調中,正常來說沒有問題;
但在我們設計LiveDataBus總線消息機制的時候,很明顯是不符合消息的流程的,不論是Rxjava還是EventBus都是不允許接收到註冊前的信息的
具體修改得依賴反射,自定義LiveData繼承自MutableLiveData,重寫observer方法
@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);
}
反射修改LiveData的mVersion值,然後通過反射拿到wrapper對象,反射修改這個對象的mLastVersion值
常規LiveDataBus配置
public class LiveDataBus {
private static LiveDataBus instance = new LiveDataBus();
private Map<String, MLiveData> map = new HashMap<>();
public static LiveDataBus getInstance() {
return instance;
}
public <T> MLiveData<T> getLiveData(String key, Class<T> type) {
if (!map.containsKey(key)) {
map.put(key, new MLiveData());
}
return (MLiveData<T>) map.get(key);
}
public void remove(String key) {
map.remove(key);
}
}
public class MLiveData<T> extends MutableLiveData<T> {}
頁面配置
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data_bus);
findViewById(R.id.btn_click).setOnClickListener(this);
textView = findViewById(R.id.tv_msg);
liveData = LiveDataBus.getInstance().getLiveData("1234", String.class);
liveData.setValue("aaa");
liveData.setValue("bbbb");
liveData.observe(this, x -> {
textView.setText(x + "");
}
);
}
這時候進來,頁面會直接顯示文字 "bbbb" ,但我們註冊是在發送之後,不應該接收到前面的消息
然後我們反射修改成這個樣式就可以了
public class MLiveData<T> extends MutableLiveData<T> {
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
super.observe(owner, observer);
reflect(observer);
}
private void reflect(Observer observer) {
try {
Field mObserversField = LiveData.class.getDeclaredField("mObservers");
mObserversField.setAccessible(true);
Object mObservers = mObserversField.get(this);
Method getMethod = mObservers.getClass().getDeclaredMethod("get", Object.class);
getMethod.setAccessible(true);
Object observerEntry = getMethod.invoke(mObservers, observer);
//這個值獲取到的是 SafeIterableMap.Entry 實現了Map.Entry接口,需要再次獲取
//而HashMap中
// public V get(Object key) {
// Node<K,V> e;
// return (e = getNode(hash(key), key)) == null ? null : e.value;
//}
// 可以看出幫我們自動獲取了value的屬性
Map.Entry entry = (Map.Entry) observerEntry;
Object observerWrapper = entry.getValue();
Field mLastVersionField = observerWrapper.getClass().getSuperclass().getDeclaredField("mLastVersion");
mLastVersionField.setAccessible(true);
mLastVersionField.set(observerWrapper, -1);
Field mVersionField = LiveData.class.getDeclaredField("mVersion");
mVersionField.setAccessible(true);
mVersionField.set(this, -1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意mObserver的get方法獲取到的並不是實際的值
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
這個集合的get方法返回的是一個節點的信息
protected Entry<K, V> get(K k) {
Entry<K, V> currentNode = mStart;
while (currentNode != null) {
if (currentNode.mKey.equals(k)) {
break;
}
currentNode = currentNode.mNext;
}
return currentNode;
}
需要再次調用獲取value方法獲取真正的值
而HashMap中
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
可以看出幫我們自動獲取了value的屬性,這兩點區分一下
LiveData總結
1.通過observe方法註冊並傳入當前Activity信息,可以感知當期頁面生命週期變化;
當頁面銷燬時候清除所有註冊並直接退出;
當頁面從不可見變成可見時,檢測數據的Version, 根據具體的比較值判斷是否發送最新消息給觀察者對象,判斷標準是當mVersion> mLastVersion 則發送,並把mLastVersion同步成mVersion值,而這兩個對應的初始值都是-1;
mVersion歸屬LiveData,而mLastVersion歸屬ObserverWrapper,對應於上面創建的LifecycleBoundObserver,反射時候需要注意;
引入Version判斷是爲了避免重複發送數據,當活躍狀態頻繁變更而數據未變更導致的多次刷新;
可見性狀態判斷只要是根據聲明的枚舉類型,當前狀態和STARTED狀態進行對比,大於這個狀態即RESUME狀態則認爲是從不可見變成可見,反之則是可見變成不可見,這個屬於被動更新;可見性又可以稱爲活動狀態,意思都差不多
2. 通過setValue或postValue方法通知所有註冊的觀察者數據發生了更新,這個是主動更新,但仍受頁面可見性狀態影響,非可見狀態並不會通知數據更新,需要等下次頁面活動狀態變更纔去刷新;
每次通知都會把當前LiveData的數據版本mVersion++,不論是被動還是主動更新都必須滿足version判定規則;
setValue在主線程中調用,postValue在子線程中調用
3.當註冊前就已經發送了消息更新數據,那麼註冊後第一次活躍狀態變更就會把這個數據刷新給觀察對象;
正常流程無需關心,但涉及消息總線處理則需要額外反射處理LiveData,一般自定義繼承MutableLiveData,在註冊的時候將傳入的對象的observer中的mLastVersion和當前LiveData中的mVersion值修改成初始值-1,這樣註冊之前的數據變更消息便不會接收