目录
- 前言
- 一、LiveData粘性原因分析
- 1.1 发送消息流程
- 1.2 监听消息流程
- 1.3 根因分析
- 二、hook解决
前言
在 Android 中,LiveData 的默认行为是粘性的,即 LiveData 在设置数据后,即使观察者订阅时已经有数据存在,观察者仍会立即收到这个数据。
在上一篇文章中JetPack之LiveData最后的案例我们看到了livedata的粘性事件
,一般情况下,我们观察者先订阅,等到消息发生改变时,接收到消息,使用observe做一些更新UI等操作,但是粘性事件会导致我们会接收到订阅之前的数据,这在某些场景下并不是我们想要的。
一、LiveData粘性原因分析
1.1 发送消息流程
MutableLiveData
的两个发送消息流程setValue、postValue。
setValue 只能在主线程使用,postValue可以在任何线程使用,它被调用时,其实也是通过handler切换到了主线程,再调用 的setValue
protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);}private final Runnable mPostValueRunnable = new Runnable() {@SuppressWarnings("unchecked")@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}setValue((T) newValue);}};
setValue 首先声明自己要在主线程中运行,然后 mVersion
++;,最后调用dispatchingValue分发消息
protected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);}
dispatchingValue
方法主要是对参数中的观察者进行了判空以及遍历,最后对每个遍历的对象调用了considerNotify
方法
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;}
considerNotify
首先判断观察者是否存活,如果观察者不处于活动状态,则直接返回,不通知观察者。
- 检查观察者是否应该处于活动状态。如果观察者不应该处于活动状态,则调用 activeStateChanged(false) 方法通知观察者状态已更改,并返回,不通知观察者。
检查观察者上一次接收到的版本号是否大于或等于 LiveData 的当前版本号。如果是,则表示观察者已经接收过最新的数据,无需再次通知观察者。
- 如果不是最新版本号,将 LiveData 的当前版本号赋值给观察者的上一次版本号,表示观察者已经接收到最新的数据。
- 调用观察者的 onChanged 方法,将 LiveData 中存储的数据 mData 传递给观察者进行处理。
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;observer.mObserver.onChanged((T) mData);}
发送消息流程基本解读完毕,读到这里,有几个核心点:
1.
mLastVersion
:上一个版本号
2.mVersion
当前版本号
3.如果当前版本号不是最新版本号,那么版本号会被覆盖,然后回调观察者的onChanged方法,即我们更新UI等操作的地方
接下来看监听流程
1.2 监听消息流程
从监听的observe方法入手
- assertMainThread(“observe”);: 检查当前线程是否为主线程,如果不是主线程则抛出异常。这是为了确保 observe 方法在主线程中调用,因为 LiveData 的观察者通常在主线程中更新 UI。
- if (owner.getLifecycle().getCurrentState() == DESTROYED) { return; }: 检查生命周期所有者的当前状态是否为 DESTROYED(已销毁),如果是,则直接返回,不执行后续操作。
- LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);: 创建一个 LifecycleBoundObserver 对象,将生命周期所有者和观察者传入。
- ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);: 将观察者和对应的 LifecycleBoundObserver 对象放入 mObservers Map 中,如果之前已经存在相同的观察者则返回已存在的 ObserverWrapper 对象。
- 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);: 将 LifecycleBoundObserver 对象添加到生命周期所有者的 Lifecycle 中,这样当生命周期所有者的状态发生变化时,会通知绑定的观察者。
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}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);}
从 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);入手,看一下内部做了什么
LifecycleBoundObserver 继承了 ObserverWrapper ,实现了 LifecycleEventObserver 接口。
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}.......
LifecycleEventObserver 当 lifecycle 状态改变的时候会感应到,并进行回调onStateChanged方法
public fun interface LifecycleEventObserver : LifecycleObserver {/*** Called when a state transition event happens.** @param source The source of the event* @param event The event*/public fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event)
}
当onStateChanged的状态改变传递到LifecycleBoundObserver时,会调用LifecycleBoundObserver的onStateChanged方法
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();if (currentState == DESTROYED) {removeObserver(mObserver);return;}Lifecycle.State prevState = null;while (prevState != currentState) {prevState = currentState;activeStateChanged(shouldBeActive());currentState = mOwner.getLifecycle().getCurrentState();}}
最终回调到ObserverWrapper 的activeStateChanged方法,观察一下ObserverWrapper类结构,原来之前的mLastVersion
是在这里定义的,默认值为-1,回到activeStateChanged方法,可以看到最终也会回到 dispatchingValue方法,只是负责分发当前观察者(this),不像发送消息流程分发到全部的观察者。
public abstract class LiveData<T> {@SuppressWarnings("WeakerAccess") /* synthetic access */final Object mDataLock = new Object();static final int START_VERSION = -1;@SuppressWarnings("WeakerAccess") /* synthetic access */
private abstract class ObserverWrapper {final Observer<? super T> mObserver;boolean mActive;int mLastVersion = START_VERSION;ObserverWrapper(Observer<? super T> observer) {mObserver = observer;}abstract boolean shouldBeActive();boolean isAttachedTo(LifecycleOwner owner) {return false;}void detachObserver() {}void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// immediately set active state, so we'd never dispatch anything to inactive// ownermActive = newActive;changeActiveCounter(mActive ? 1 : -1);if (mActive) {dispatchingValue(this);}}}
1.3 根因分析
mLastVersion 的默认初始值是-1,mVersion 的默认初始值也是-1,当我们先执行发送的时候,进行了自增,mVersion 就变成了0,当我们执行observe 进行监听的时候,observer.mLastVersion >= mVersion 这个条件就不成立了,因为此时mLastVersion 是-1,小于 mVersion 了。
发送和监听都会调用dispatchingValue
方法,但mVersion
只要发送就会在setValue方法中++,而mLastVersion
永远只能在setValue方法后的considerNotify方法中被置为mVersion的值。
public LiveData() {mData = NOT_SET;mVersion = START_VERSION;}public abstract class LiveData<T> {@SuppressWarnings("WeakerAccess") /* synthetic access */final Object mDataLock = new Object();static final int START_VERSION = -1;
二、hook解决
在安卓开发中,“Hook” 是指通过修改系统或应用程序的行为,来实现某种特定的功能或者改变程序的默认行为。常见的 Hook 技术包括方法 Hook、类 Hook、系统 Hook 等。以下是对安卓 Hook 的详细解释:
方法 Hook
:
方法 Hook 是指在程序运行时替换或者修改某个方法的实现逻辑,以达到特定的目的。
通过方法 Hook,可以拦截系统或第三方库的方法调用,修改方法的参数或返回值,实现功能增强或者数据篡改等操作。
常见的方法 Hook 框架有 Xposed、Dexposed、Frida 等。
类 Hook
:
类 Hook 是指在程序运行时替换或者修改某个类的实现逻辑,以达到特定的目的。
通过类 Hook,可以修改类的属性、方法行为,实现功能增强或者数据篡改等操作。
类 Hook 通常需要使用字节码操作技术,如 ASM、Javassist 等。
系统 Hook
:
系统 Hook 是指修改系统层的行为,如修改系统服务、系统调用等,以实现对系统行为的控制。
通过系统 Hook,可以实现系统级别的功能增强、权限管理、安全加固等操作。
系统 Hook 需要对系统底层进行深入了解,通常需要 root 权限才能实现。
Hook 的应用场景:
功能增强:通过 Hook 修改系统或应用程序的行为,实现功能增强或定制化功能。
数据篡改:通过 Hook 修改数据传递或处理逻辑,实现数据篡改或数据劫持。
安全加固:通过 Hook 检测恶意行为、加固系统安全,防止恶意软件的攻击。
调试分析:通过 Hook 获取程序运行时的信息,进行调试分析或性能优化。
我们使用hook反射的方式,在每次Observe方法中,将mLastVersion
赋值为mVersion
,这样下次就不会调用到onChanged
方法中,去除了粘性!
核心代码
private void hook(Observer<? super T> observer) {try {Field mObserversField = LiveData.class.getDeclaredField("mObservers");mObserversField.setAccessible(true);Object mObserversObject = mObserversField.get(this);Class<?> mObserversClass = mObserversObject.getClass();Method get = mObserversClass.getDeclaredMethod("get", Object.class);get.setAccessible(true);Object invokeEntry = get.invoke(mObserversObject, observer);Object observerWrapper = null;if (invokeEntry != null && invokeEntry instanceof Map.Entry) {observerWrapper = ((Map.Entry) invokeEntry).getValue();}if (observerWrapper == null) {throw new NullPointerException("observerWrapper is null");}Log.d("Henry","属性是什么"+ observerWrapper.getClass());Class<?> superClass = observerWrapper.getClass().getSuperclass();Field mLastVersion = superClass.getDeclaredField("mLastVersion");mLastVersion.setAccessible(true);Field mVersion = LiveData.class.getDeclaredField("mVersion");mVersion.setAccessible(true);Object mVersionValue = mVersion.get(this);mLastVersion.set(observerWrapper, mVersionValue);} catch (Exception e) {e.printStackTrace();}}
示例:采取反射,先发消息后订阅,不会接受到旧消息。
OkLiveDataBusJava.java
public class OkLiveDataBusJava {//存放订阅者private static final Map<String, BusMutableLiveData<?>> bus = new HashMap<>();public synchronized static <T> BusMutableLiveData<T> with(String key, Class<T> type, boolean ishook) {if (!bus.containsKey(key)) {bus.put(key, new BusMutableLiveData<>(ishook));}return (BusMutableLiveData<T>) bus.get(key);}public static class BusMutableLiveData<T> extends MutableLiveData<T> {private boolean ishook;private BusMutableLiveData(boolean ishook) {this.ishook = ishook;}@Overridepublic void observe(LifecycleOwner owner, Observer<? super T> observer) {super.observe(owner, observer);if (ishook) {hook(observer);Log.d("Henry", " 启用hook");} else {Log.d("Henry", " 不启用hook");}}private void hook(Observer<? super T> observer) {try {Field mObserversField = LiveData.class.getDeclaredField("mObservers");mObserversField.setAccessible(true);Object mObserversObject = mObserversField.get(this);Class<?> mObserversClass = mObserversObject.getClass();Method get = mObserversClass.getDeclaredMethod("get", Object.class);get.setAccessible(true);Object invokeEntry = get.invoke(mObserversObject, observer);Object observerWrapper = null;if (invokeEntry != null && invokeEntry instanceof Map.Entry) {observerWrapper = ((Map.Entry) invokeEntry).getValue();}if (observerWrapper == null) {throw new NullPointerException("observerWrapper is null");}Log.d("Henry","属性是什么"+ observerWrapper.getClass());Class<?> superClass = observerWrapper.getClass().getSuperclass();Field mLastVersion = superClass.getDeclaredField("mLastVersion");mLastVersion.setAccessible(true);Field mVersion = LiveData.class.getDeclaredField("mVersion");mVersion.setAccessible(true);Object mVersionValue = mVersion.get(this);mLastVersion.set(observerWrapper, mVersionValue);} catch (Exception e) {e.printStackTrace();}}}
}
OkLiveDataBusActivity
public class OkLiveDataBusActivity extends AppCompatActivity {Button button;@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_ok_live_data_bus);button = findViewById(R.id.OKlivedata_jump);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(OkLiveDataBusActivity.this,OkLiveDataBusSecondActivity.class));}});OkLiveDataBusJava.with("data", String.class, true).postValue("old 数据-----------");}
}
OkLiveDataBusSecondActivity
public class OkLiveDataBusSecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_ok_live_data_bus_second);OkLiveDataBusJava.with("data", String.class, true).observe(this,new Observer<String>() {@Overridepublic void onChanged(String s) {Toast.makeText(OkLiveDataBusSecondActivity.this,"获取数据" + s, Toast.LENGTH_SHORT).show();}});new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}OkLiveDataBusJava.with("data", String.class, true).postValue("new 数据-----------");}}).start();}
}
测试一下:
来张美图犒劳一下