前言
何為粘性事件?
即發(fā)射的事件如果早于注冊山叮,那么注冊之后依然可以接收到的事件稱為粘性事件
背景
最近接手了一個公司的項目,采用了目前比較新的技術(shù):LiveData+ViewModel的事件通知框架。該框架擁有大量的優(yōu)點包括但不僅限于以下:1.實時感知生命周期柬姚。2.無需手動回收,解綁,即不會出現(xiàn)內(nèi)存泄漏的情況庄涡。3.數(shù)據(jù)變化可進行實時通知 等等......
本人對于這個框架也只是一知半解量承,奈何項目比較緊急,接手以后馬上就要開始干活穴店,所以只能是邊干活邊了解內(nèi)部實現(xiàn)原理撕捍。
結(jié)果項目中就碰到了一個讓我頭疼了整整一天的問題。那么到底是啥呢泣洞,請接著往下看↓
起因
由于公司代碼不便放到網(wǎng)上忧风,所以本人便以demo代碼代替(主要邏輯一致)
public class CustomViewModel extends ViewModel {
MutableLiveData<Integer> mLiveData;
private int mPostedValue = 10;
public MutableLiveData<Integer> getLiveData(){
if(mLiveData == null){
mLiveData = new MutableLiveData<>();
}
loadData();
return mLiveData;
}
private void loadData() {
new Thread(){
@Override
public void run() {
SystemClock.sleep(2000);
mLiveData.postValue(mPostedValue);
mPostedValue = mPostedValue * 2;
}
}.start();
}
}
這個就是ViewModel的代碼,用于進行網(wǎng)絡(luò)請求的操作球凰,返回數(shù)據(jù)以后通過LiveData實時刷新
public class MainActivity extends AppCompatActivity {
private CustomViewModel mViewModel;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this).get(CustomViewModel.class);
final MutableLiveData<Integer> liveData = mViewModel.getLiveData();
liveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e(TAG,"參數(shù)返回: " + integer);
}
});
}
@Override
protected void onResume() {
super.onResume();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
MutableLiveData<Integer> liveData = mViewModel.getLiveData();
liveData.observe(MainActivity.this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e(TAG,"參數(shù)返回: " + integer);
}
});
}
},5000);
}
}
此為activity里面的代碼狮腿,onCreate會率先訂閱一個LiveData的事件,然后觀察網(wǎng)絡(luò)請求回調(diào)呕诉。onResume里面的代碼模仿的就是點擊事件再次進行網(wǎng)絡(luò)請求缘厢。
現(xiàn)象
打印結(jié)果如下
2019-09-14 18:48:23.710 6745-6745/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 10
2019-09-14 18:48:26.720 6745-6745/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 10
2019-09-14 18:48:28.721 6745-6745/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 20
2019-09-14 18:48:28.721 6745-6745/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 20
首先看到這個結(jié)果,肯定不是我想要的結(jié)果甩挫。下面的20打印了兩次贴硫,不過這個現(xiàn)象倒是一眼就能看出來,畢竟onCreate的時候已經(jīng)訂閱過一次伊者,你發(fā)射的第二次數(shù)據(jù)自然就會有兩個觀察者可以監(jiān)聽到英遭。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this).get(CustomViewModel.class);
final MutableLiveData<Integer> liveData = mViewModel.getLiveData();
liveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e(TAG,"參數(shù)返回: " + integer);
//解除觀察者
liveData.removeObserver(this);
}
});
}
所以在onCreate的注冊方法里面加上了解除注冊的操作拖刃。嗯,完美了贪绘,打印下看看
2019-09-14 18:54:17.465 6973-6973/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 10
2019-09-14 18:54:20.483 6973-6973/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 10
2019-09-14 18:54:22.483 6973-6973/com.netease.livedatademo E/MainActivity: 參數(shù)返回: 20
事情總是不能如人所愿兑牡。。税灌。發(fā)現(xiàn)這時候10依然多打了一次均函,那么問題來了,這個10到底是哪里來的菱涤。(我在公司代碼里面全局搜索了一遍苞也,發(fā)射數(shù)據(jù)的地方就這么一個,于是就很費解到底怎么回事)
既然事情結(jié)果是這樣了粘秆,肯定不能就這么下去如迟,于是開始了源碼探究,既然onCreate里面的注冊已經(jīng)解除攻走,那么基本沒什么必要在往下看下去了殷勘,所以打算從onResume調(diào)用的注冊方法開始下手
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在調(diào)用Observer以后最后會執(zhí)行addObserver方法,而此處getLifeCycle獲取的對象是LifecycleRegistry對象(原因不過多追究昔搂,不是本文重點)
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
...代碼省略...
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
...代碼省略...
}
此處會進入while循環(huán)玲销,然后調(diào)用dispatchEvent方法,所以我們接著往下看
static class ObserverWithState {
State mState;
GenericLifecycleObserver mLifecycleObserver;
ObserverWithState(LifecycleObserver observer, State initialState) {
mLifecycleObserver = Lifecycling.getCallback(observer);
mState = initialState;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = getStateAfter(event);
mState = min(mState, newState);
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
}
然后會調(diào)用LifeCycleBoundObserver的onStateChanged方法
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
}
private abstract class ObserverWrapper {
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);
}
}
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
...代碼省略
do {
mDispatchInvalidated = false;
if (initiator != null) {
//會執(zhí)行此方法
considerNotify(initiator);
initiator = null;
}
...代碼省略
}
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);
}
最終會判斷mLastVersion是否比mVersion大摘符,如果小于mVersion,那么會調(diào)用onChanged方法贤斜,即我們在MainActivity里面注冊的事件。那么mLastVersion和mVersion是什么逛裤,接著往下看:
private int mVersion = START_VERSION;
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
我們發(fā)現(xiàn)mVersion初始值是-1瘩绒,這個mVersion是屬于LiveData的,然后只在setValue(postValue最終也會調(diào)用setValue)的時候會自增1,那么mLastVersion呢带族?
int mLastVersion = START_VERSION;
發(fā)現(xiàn)mLastVersion也是初始值為-1锁荔,而這個mLastVersion是屬于ObserverWrapper的,而賦值的地方只有在比較完才會賦值.
那么仔細回想下,我們其實在onCreate那里注冊的觀察者信息其實有過一次setValue的操作了炉菲,又因為我們其實用的是同一個LiveData堕战,所以mVersion最后是會自增1的,又因為ObserverWrapper在每次注冊的時候都會重新new拍霜,所以mLastVersion每次都是-1開始嘱丢。
那么真相大白了,只要之前有發(fā)射過一次數(shù)據(jù)祠饺,那么后面注冊的觀察者都會接收到之前發(fā)射過的數(shù)據(jù)越驻,而且看樣子這個Version值不可以輕易改變,也就是說谷歌不提供API讓我們?nèi)∠粽承允录?這算是一個比較大的缺點了)
解決方案
問題來了,既然已經(jīng)知道了原因缀旁,那么怎么解決呢记劈?不可能放著不管的。方案有三并巍,容我一一道來
1.既然每次注冊時ObserverWrapper是不一樣的目木,那么只要我們的LiveData也不一樣不就可以輕松解決了?
2.不要多次注冊:onCreate里面的注冊以后懊渡,onResume里面就不要注冊了刽射,然后通過判斷條件的不同寫兩個不同的處理方式。
3.既然無法直接修改mVersion值剃执,和mLastVersion值誓禁。那么我們可以直接重寫LiveData類。然后重寫Observer接口肾档,通過重寫的Observer類的onChange方法進行攔截摹恰。那么怎么攔截?
在LiveData的Observe方法里面將傳入的Observer對象裝飾到我們自己的Observer類里面怒见,然后調(diào)用super.Observe的時候?qū)⑽覀冏约旱腛bserver方法傳入俗慈,然后就可以進行自定義攔截。代碼如下:
public class BaseLiveData<T> extends MutableLiveData<T> {
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer,boolean isSticky) {
if(isSticky){
super.observe(owner, observer);
} else {
super.observe(owner,new CustomObserver<T>(observer));
}
}
@Override
public void setValue(T value) {
super.setValue(value);
}
@Override
public void postValue(T value) {
super.postValue(value);
}
class CustomObserver<T> implements Observer<T> {
private Observer<? super T> mObserver;
public CustomObserver(Observer<? super T> observer) {
mObserver = observer;
}
@Override
public void onChanged(T t) {
//此處做攔截操作
mObserver.onChanged(t);
}
}
}
總結(jié)
總的來說解決方案不算復(fù)雜速种,前面兩種更是沒有什么難度姜盈。只不過谷歌不提供API讓我們可以解除LiveData的粘性事件確實有點霸道。所以就這點來說遠沒有EventBust來的靈活配阵,當(dāng)然LiveData生命感知的能力確實是EventBus無法比擬的。只能說各取所需吧