解决 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<T> {
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<String, BusMutableLiveData<Object>> map; //存储所有 LiveData 的容器
private LiveDataBus(){
map = new HashMap<>();
}
public static LiveDataBus getInstance(){
return liveDataBus;
}
/**
* 存和取一体的方法, 这里传入了 type 却又不使用, 是因为要使用泛型 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技术的实现方法 拦截onChange方法的执行
* @param observer
*/
private void hook(Observer<? super T> observer) throws Exception {
//获取到LiveData的类对象
Class<LiveData> liveDataClass = LiveData.class;
//根据类对象获取到mVersion的反射对象
Field mVersionField = liveDataClass.getDeclaredField("mVersion");
//打开权限
mVersionField.setAccessible(true);
//获取到mObservers的反射对象
Field mObserversField = liveDataClass.getDeclaredField("mObservers");
//打开权限
mObserversField.setAccessible(true);
//从当前的LiveData对象中获取mObservers这个成员变量在当前对象中的值
Object mObservers = mObserversField.get(this);
//获取到mObservers这个map的get方法
Method get = mObservers.getClass().getDeclaredMethod("get", Object.class);
//打开权限
get.setAccessible(true);
// 执行get方法
Object invokeEntry = get.invoke(mObservers, observer);
//定义一个空对象 LifecycleBoundObserver
Object observerWrapper = null;
if(invokeEntry !=null && invokeEntry instanceof Map.Entry){
observerWrapper = ((Map.Entry)invokeEntry).getValue();
}
if(observerWrapper == null){
throw new NullPointerException("ObserverWrapper不能为空");
}
//得到observerWrapper的类对象
Class<?> aClass = observerWrapper.getClass().getSuperclass();
//获取mLastVersion的发射对象
Field mLastVersionField = aClass.getDeclaredField("mLastVersion");
//打开权限
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);
//设置为 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");
}
}
此时从 MainActivity 跳转到 LoginActivity 后,LoginActivity 并不会触发 observer 的 onChanged()
方法,但是点击调用 clickSendData()
方法后,仍然可以触发 observer 的 onChanged()
方法,即使用 LiveDataBus.BusMutableLiveData<String>
并设置 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<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 中根据同样的 key 创建一个 泛型为 User 的 liveData 对象
@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());
}
});
}
}
由于设置粘性事件为 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<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);
}
网友评论