解决 LiveData 粘性事件问题
1. 什么是粘性事件
从上一个 Activity 发送数据后,在下一个 Activity 注册 observer 还能收到,那么这种事件就叫做粘性事件(即允许先发送再注册就是粘性事件)。
2. observer.mLastVersion 和 mVersion 的关系
谁的 version 最大谁的数据就是最新的, 并且有 setValue()
会更新 mData 的值, 同时让 mVersion + 1, 那么有如下几种情况 :
如果 observer 的 mLastVersion 大, 那么 observer 的数据是最新的, 直接返回
如果 observer 的 mLastVersion 和 mVersion 一样大, 那么说明 observer 的数据 和 mData 的数据一样新, 直接返回
如果 observer 的 mLastVersion 比 mVersion 小, 那么说明 mData 的数据是最新的, 需要更新 ui, 同时重置 observer 的 mLastVersion 为 mVersion (因为此时数据一样新了)
3. 解决 LiveData 粘性事件的思路
使用反射让 observer.mLastVersion == mVersion
源码解析
public abstract class LiveData {
private void considerNotify(ObserverWrapper observer) {
//...
//observer 的 mLastVersion 比 mVersion 大 (observer 的数据是最新的)
//observer 的 mLastVersion 和 mVersion 一样大 (observer 的数据 和 mData 的数据一样新)
if (observer.mLastVersion >= mVersion) {
return;
}
//mData 的数据是 最新的
observer.mLastVersion = mVersion;
//noinspection unchecked
//观察者就是 observer.mObserver, 调用其 onChanged() 方法
observer.mObserver.onChanged((T) mData);
}
}
4. 实例代码
在 LiveDataBus 中重写 MutableLiveData
类,并将之前的 MutableLiveData
替换为 BusMutableLiveData
:
public class LiveDataBus {
private static LiveDataBus liveDataBus = new LiveDataBus();
private Map> map; //存储所有 LiveData 的容器
private LiveDataBus(){
map = new HashMap<>();
}
public static LiveDataBus getInstance(){
return liveDataBus;
}
/**
* 存和取一体的方法, 这里传入了 type 却又不使用, 是因为要使用泛型 T 而不是 type
* @param key
* @param type
* @param
* @return
*/
public synchronized BusMutableLiveData with(String key,Class type){
if(!map.containsKey(key)){
map.put(key,new BusMutableLiveData
LoginActivity 中将 MutableLiveData
替换为 LiveDataBus.BusMutableLiveData
:
@BindPath("login/login")
public class LoginActivity extends AppCompatActivity {
LiveDataBus.BusMutableLiveData liveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
liveData = LiveDataBus.getInstance().with("lisan", String.class);
//设置为 true 表示不接收粘性事件
this.liveData.observe(LoginActivity.this, true, new Observer() {
@Override
public void onChanged(String s) {
Log.e("LoginActivity-->",s);
}
});
}
public void clickSendData(View view){
liveData.postValue("LoginActivity clickSendData");
}
}
此时从 MainActivity 跳转到 LoginActivity 后,LoginActivity 并不会触发 observer 的 onChanged()
方法,但是点击调用 clickSendData()
方法后,仍然可以触发 observer 的 onChanged()
方法,即使用 LiveDataBus.BusMutableLiveData
并设置 isViscosity = true
并不会妨碍后边的消息接收。
5. LiveDataBus 使用一种 key 创建 liveData 对象导致的类型转换异常
在 MainActivity 中使用 LiveDataBus 根据 key="lisan"
创建一个泛型为 String 的 liveData 对象, 然后使用 liveData.postValue("MainActivity postValue");
发送一个字符串 :
@BindPath("main/main")
public class MainActivity extends AppCompatActivity {
MutableLiveData 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 中根据同样的 key 创建一个 泛型为 User 的 liveData 对象
@BindPath("login/login")
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
LiveDataBus.BusMutableLiveData lisan = LiveDataBus.getInstance().with("lisan", User.class);
lisan.observe(LoginActivity.this, false, new Observer() {
@Override
public void onChanged(User user) {
Log.e("LoginActivity-->", user.getName());
}
});
}
}
由于设置粘性事件为 false (需要粘性事件),导致运行时报异常 :
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)
因为 this.mData
是 MainActivity 中设置的 String 对象,当调用 observer.mObserver.onChanged(this.mData)
将 String 对象强制转换为 User 时就会报异常。
public abstract class LiveData {
//...
private void considerNotify(LiveData.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 {
void onChanged(T var1);
}