解決 LiveData 粘性事件問題
1. 什么是粘性事件
從上一個(gè) Activity 發(fā)送數(shù)據(jù)后竣付,在下一個(gè) Activity 注冊(cè) observer 還能收到,那么這種事件就叫做粘性事件(即允許先發(fā)送再注冊(cè)就是粘性事件)竭钝。
2. observer.mLastVersion 和 mVersion 的關(guān)系
誰的 version 最大誰的數(shù)據(jù)就是最新的, 并且有 setValue()
會(huì)更新 mData 的值, 同時(shí)讓 mVersion + 1, 那么有如下幾種情況 :
如果 observer 的 mLastVersion 大, 那么 observer 的數(shù)據(jù)是最新的, 直接返回
如果 observer 的 mLastVersion 和 mVersion 一樣大, 那么說明 observer 的數(shù)據(jù) 和 mData 的數(shù)據(jù)一樣新, 直接返回
如果 observer 的 mLastVersion 比 mVersion 小, 那么說明 mData 的數(shù)據(jù)是最新的, 需要更新 ui, 同時(shí)重置 observer 的 mLastVersion 為 mVersion (因?yàn)榇藭r(shí)數(shù)據(jù)一樣新了)
3. 解決 LiveData 粘性事件的思路
使用反射讓 observer.mLastVersion == mVersion
源碼解析
public abstract class LiveData<T> {
private void considerNotify(ObserverWrapper observer) {
//...
//observer 的 mLastVersion 比 mVersion 大 (observer 的數(shù)據(jù)是最新的)
//observer 的 mLastVersion 和 mVersion 一樣大 (observer 的數(shù)據(jù) 和 mData 的數(shù)據(jù)一樣新)
if (observer.mLastVersion >= mVersion) {
return;
}
//mData 的數(shù)據(jù)是 最新的
observer.mLastVersion = mVersion;
//noinspection unchecked
//觀察者就是 observer.mObserver, 調(diào)用其 onChanged() 方法
observer.mObserver.onChanged((T) mData);
}
}
4. 實(shí)例代碼
在 LiveDataBus 中重寫 MutableLiveData
類,并將之前的 MutableLiveData
替換為 BusMutableLiveData
:
public class LiveDataBus {
private static LiveDataBus liveDataBus = new LiveDataBus();
private Map<String, BusMutableLiveData<Object>> map; //存儲(chǔ)所有 LiveData 的容器
private LiveDataBus(){
map = new HashMap<>();
}
public static LiveDataBus getInstance(){
return liveDataBus;
}
/**
* 存和取一體的方法, 這里傳入了 type 卻又不使用, 是因?yàn)橐褂梅盒?T 而不是 type
* @param key
* @param type
* @param <T>
* @return
*/
public synchronized<T> BusMutableLiveData<T> with(String key,Class<T> type){
if(!map.containsKey(key)){
map.put(key,new BusMutableLiveData<Object>());
}
return (BusMutableLiveData<T>) map.get(key);
}
public class BusMutableLiveData<T> extends MutableLiveData<T> {
//false; 需要粘性事件 true 不需要粘性事件
private boolean isViscosity = false;
public void observe(@NonNull LifecycleOwner owner, boolean isViscosity, @NonNull Observer<T> observer) {
this.isViscosity = isViscosity;
observe(owner, observer);
}
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, observer);
try {
if(isViscosity){
//通過反射 獲取到mVersion 獲取到mLastVersion 將mVersion 的值給mLastVersion
hook(observer);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* hook技術(shù)的實(shí)現(xiàn)方法 攔截onChange方法的執(zhí)行
* @param observer
*/
private void hook(Observer<? super T> observer) throws Exception {
//獲取到LiveData的類對(duì)象
Class<LiveData> liveDataClass = LiveData.class;
//根據(jù)類對(duì)象獲取到mVersion的反射對(duì)象
Field mVersionField = liveDataClass.getDeclaredField("mVersion");
//打開權(quán)限
mVersionField.setAccessible(true);
//獲取到mObservers的反射對(duì)象
Field mObserversField = liveDataClass.getDeclaredField("mObservers");
//打開權(quán)限
mObserversField.setAccessible(true);
//從當(dāng)前的LiveData對(duì)象中獲取mObservers這個(gè)成員變量在當(dāng)前對(duì)象中的值
Object mObservers = mObserversField.get(this);
//獲取到mObservers這個(gè)map的get方法
Method get = mObservers.getClass().getDeclaredMethod("get", Object.class);
//打開權(quán)限
get.setAccessible(true);
// 執(zhí)行g(shù)et方法
Object invokeEntry = get.invoke(mObservers, observer);
//定義一個(gè)空對(duì)象 LifecycleBoundObserver
Object observerWrapper = null;
if(invokeEntry !=null && invokeEntry instanceof Map.Entry){
observerWrapper = ((Map.Entry)invokeEntry).getValue();
}
if(observerWrapper == null){
throw new NullPointerException("ObserverWrapper不能為空");
}
//得到observerWrapper的類對(duì)象
Class<?> aClass = observerWrapper.getClass().getSuperclass();
//獲取mLastVersion的發(fā)射對(duì)象
Field mLastVersionField = aClass.getDeclaredField("mLastVersion");
//打開權(quán)限
mLastVersionField.setAccessible(true);
//獲取到mVersion的值
Object o = mVersionField.get(this);
//把它的值賦值給mLastVersion
mLastVersionField.set(observerWrapper,o);
}
}
}
LoginActivity 中將 MutableLiveData
替換為 LiveDataBus.BusMutableLiveData<String>
:
@BindPath("login/login")
public class LoginActivity extends AppCompatActivity {
LiveDataBus.BusMutableLiveData<String> liveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
liveData = LiveDataBus.getInstance().with("lisan", String.class);
//設(shè)置為 true 表示不接收粘性事件
this.liveData.observe(LoginActivity.this, true, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("LoginActivity-->",s);
}
});
}
public void clickSendData(View view){
liveData.postValue("LoginActivity clickSendData");
}
}
此時(shí)從 MainActivity 跳轉(zhuǎn)到 LoginActivity 后雹洗,LoginActivity 并不會(huì)觸發(fā) observer 的 onChanged()
方法香罐,但是點(diǎn)擊調(diào)用 clickSendData()
方法后,仍然可以觸發(fā) observer 的 onChanged()
方法时肿,即使用 LiveDataBus.BusMutableLiveData<String>
并設(shè)置 isViscosity = true
并不會(huì)妨礙后邊的消息接收庇茫。
5. LiveDataBus 使用一種 key 創(chuàng)建 liveData 對(duì)象導(dǎo)致的類型轉(zhuǎn)換異常
在 MainActivity 中使用 LiveDataBus 根據(jù) key="lisan"
創(chuàng)建一個(gè)泛型為 String 的 liveData 對(duì)象, 然后使用 liveData.postValue("MainActivity postValue");
發(fā)送一個(gè)字符串 :
@BindPath("main/main")
public class MainActivity extends AppCompatActivity {
MutableLiveData<String> liveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
liveData = LiveDataBus.getInstance().with("lisan", String.class);
liveData.postValue("MainActivity postValue");
}
}
但是在 LoginActivity 中根據(jù)同樣的 key 創(chuàng)建一個(gè) 泛型為 User 的 liveData 對(duì)象
@BindPath("login/login")
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
LiveDataBus.BusMutableLiveData<User> lisan = LiveDataBus.getInstance().with("lisan", User.class);
lisan.observe(LoginActivity.this, false, new Observer<User>() {
@Override
public void onChanged(User user) {
Log.e("LoginActivity-->", user.getName());
}
});
}
}
由于設(shè)置粘性事件為 false (需要粘性事件),導(dǎo)致運(yùn)行時(shí)報(bào)異常 :
java.lang.ClassCastException: java.lang.String cannot be cast to com.maniu.login.User
at com.maniu.login.LoginActivity$1.onChanged(LoginActivity.java:34)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:113)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:126)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:424)
at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:376)
因?yàn)?this.mData
是 MainActivity 中設(shè)置的 String 對(duì)象螃成,當(dāng)調(diào)用 observer.mObserver.onChanged(this.mData)
將 String 對(duì)象強(qiáng)制轉(zhuǎn)換為 User 時(shí)就會(huì)報(bào)異常旦签。
public abstract class LiveData<T> {
//...
private void considerNotify(LiveData<T>.ObserverWrapper observer) {
if (observer.mActive) {
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
} else if (observer.mLastVersion < this.mVersion) {
observer.mLastVersion = this.mVersion;
observer.mObserver.onChanged(this.mData);
}
}
}
}
public interface Observer<T> {
void onChanged(T var1);
}