请点赞,你的点赞对我意义重大,满足下我的虚荣心。
Hi,我是小彭。本文已收录到 GitHub · Android-NoteBook 中。这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] 跟我一起成长。
这篇文章是 Jetpack 系列文章第 2 篇,专栏文章列表:
一、架构组件:
二、其他:
LiveData 是基于 Lifecycle 框架实现的生命周期感知型数据容器,能够让数据观察者更加安全地应对宿主(Activity / Fragment 等)生命周期变化,核心概括为 2 点:
// 过时方式(lifecycle-extensions 不再维护)
implementation "androidx.lifecycle:lifecycle-extensions:2.4.0"
// 目前的方式:
def lifecycle_version = "2.5.0"
// Lifecycle 核心类
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
NameViewModel.kt
class NameViewModel : ViewModel() {
val currentName: MutableLiveData by lazy {
MutableLiveData()
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val model: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// LiveData 观察者
val nameObserver = Observer { newName ->
// 更新视图
nameTextView.text = newName
}
// 注册 LiveData 观察者,this 为生命周期宿主
model.currentName.observe(this, nameObserver)
// 修改 LiveData 数据
button.setOnClickListener {
val anotherName = "John Doe"
model.currentName.value = anotherName
}
}
}
Observer.java
// 观察者接口
public interface Observer {
void onChanged(T t);
}
MutableLiveData.java
public class MutableLiveData extends LiveData {
// 异步设置数据
@Override
public void postValue(T value) {
super.postValue(value);
}
// 同步设置数据
@Override
public void setValue(T value) {
super.setValue(value);
}
}
LiveData 是 Android 生态中一个的简单的生命周期感知型容器。简单即是它的优势,也是它的局限,当然这些局限性不应该算 LiveData 的缺点,因为 LiveData 的设计初衷就是一个简单的数据容器,需要具体问题具体分析。对于简单的数据流场景,使用 LiveData 完全没有问题。
onChanged()
回调(可以使用 distinctUntilChanged()
优化);postValue
数据但主线程消费跟不上时,中间就会有一部分数据被忽略。关于 Kotlin Flow 的更多内容,我们在 4、Flow:LiveData 的替代方案 这篇文章讨论过。
LiveData 支持使用 observe() 或 observeForever() 两种方式注册观察者,其内部会分别包装为 2 种包装对象:
LifecycleEventObserver
的实现类,因此它可以绑定到宿主(参数 owner)的生命周期上,这是实现 LiveData 自动取消订阅和安全地回调数据的关键;注意: LiveData 内部会禁止一个观察者同时使用 observe() 和 observeForever() 两种注册方式。但同一个 LiveData 可以接收 observe() 和 observeForever() 两种观察者。
LiveData.java
private SafeIterableMap, ObserverWrapper> mObservers = new SafeIterableMap<>();
// 注册方式 1:带生命周期感知的注册方式
@MainThread
public void observe(LifecycleOwner owner, Observer super T> observer) {
// 1.1 主线程检查
assertMainThread("observe");
// 1.2 宿主生命周期状态是 DESTROY,则跳过
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
// 1.3 将 Observer 包装为 LifecycleBoundObserver
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 1.4 禁止将 Observer 绑定到不同的宿主上
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
}
if (existing != null) {
return;
}
// 1.5 将包装类注册到宿主声明周期上
owner.getLifecycle().addObserver(wrapper);
}
// 注册方式 2:永久注册的方式
@MainThread
public void observeForever(Observer super T> observer) {
// 2.1 主线程检查
assertMainThread("observeForever");
// 2.2 将 Observer 包装为 AlwaysActiveObserver
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
// 2.3 禁止将 Observer 注册到生命周期宿主后又进行永久注册
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
}
if (existing != null) {
return;
}
// 2.4 分发最新数据
wrapper.activeStateChanged(true);
}
// 注销观察者
@MainThread
public void removeObserver(@NonNull final Observer super T> observer) {
// 主线程检查
assertMainThread("removeObserver");
// 移除
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
// removed.detachObserver() 方法:
// LifecycleBoundObserver 最终会调用 Lifecycle#removeObserver()
// AlwaysActiveObserver 为空实现
removed.detachObserver();
removed.activeStateChanged(false);
}
LifecycleBoundObserver 是 LifecycleEventObserver
的实现类,当宿主生命周期变化时,会回调其中的 LifecycleEventObserve#onStateChanged() 方法:
LiveData$ObserverWrapper.java
private abstract class ObserverWrapper {
final Observer super T> mObserver;
boolean mActive;
// 观察者持有的版本号
int mLastVersion = START_VERSION; // -1
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;
}
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
// STARTED 状态以上才会尝试分发数据
if (mActive) {
dispatchingValue(this);
}
}
}
Livedata$LifecycleBoundObserver.java
// 注册方式:observe()
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer super T> observer) {
super(observer);
mOwner = owner;
}
// 宿主的生命周期大于等于可见状态(STARTED),认为活动状态
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
// 宿主生命周期进入 DESTROYED 时,会移除观察者
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
// 宿主从非可见状态转为可见状态(STARTED)时,会尝试触发数据分发
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
AlwaysActiveObserver.java
// 注册方式:observeForever()
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
LiveData 使用 setValue() 方法进行同步设置数据(必须在主线程调用),需要注意的是,设置数据后并不一定会回调 Observer#onChanged() 分发数据,而是需要同时满足 2 个条件:
LiveData.java
// LiveData 持有的版本号
private int mVersion;
// 异步设置数据 postValue() 最终也是调用到 setValue()
@MainThread
protected void setValue(T value) {
// 主线程检查
assertMainThread("setValue");
// 版本号加一
mVersion++;
mData = value;
// 数据分发
dispatchingValue(null);
}
// 数据分发
void dispatchingValue(ObserverWrapper initiator) {
// 这里的标记位和嵌套循环是为了处理在 Observer#onChanged() 中继续调用 setValue(),
// 而产生的递归设置数据的情况,此时会中断旧数据的分发,转而分发新数据,这是丢失数据的第 2 种情况。
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
// onStateChanged() 走这个分支,只需要处理单个观察者
considerNotify(initiator);
initiator = null;
} else {
// setValue() 走这个分支,需要遍历所有观察者
for (Iterator, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
// 尝试触发回调,只有观察者持有的版本号小于 LiveData 持有版本号,才会分发回调
private void considerNotify(ObserverWrapper observer) {
// STARTED 状态以上才会尝试分发数据
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// 版本对比
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 分发回调
observer.mObserver.onChanged((T) mData);
}
总结一下回调 Observer#onChanged() 的情况:
提示: observeForever() 虽然没有直接绑定生命周期宿主,但可以理解为绑定的生命周期是全局的,因此在移除观察者之前都是活跃状态。
LiveData 使用 postValue() 方法进行异步设置数据(允许在子线程调用),内部会通过一个临时变量 mPendingData 存储数据,再通过 Handler 将切换到主线程并调用 setValue(临时变量)。因此,当在子线程连续 postValue() 时,可能会出现中间的部分数据不会被观察者接收到。
LiveData.java
final Object mDataLock = new Object();
static final Object NOT_SET = new Object();
// 临时变量
volatile Object mPendingData = NOT_SET;
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
// 重置临时变量
mPendingData = NOT_SET;
}
// 真正修改数据的地方,也是统一到 setValue() 设置数据
setValue((T) newValue);
}
};
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
// 临时变量被重置时,才会发送修改的 Message,这是出现背压的第 1 种情况
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
总结一下 LiveData 可能丢失数据的场景,此时观察者可能不会接收到所有的数据:
注意: 丢失数据不一定是需要解决的问题,需要视场景分析。
LiveData 的数据重放问题也叫作数据倒灌、粘性事件,核心源码在 LiveData#considerNotify(Observer) 中:
为什么 Google 要把 LiveData 设计为粘性呢?LiveData 重放问题需要区分场景来看 —— 状态适合重放,而事件不适合重放:
这里我们总结一下业界提出处理 LiveData 数据重放问题的方案:
实现一个事件包装器,内部使用一个标志位标记事件是否已经被消费过。这样的话,当观察者收到重放的数据时,由于其中的标记位已经显示被消费,因此会抛弃该事件。
不过,虽然这个方法能够解决数据倒灌问题,但是会有副作用:对于多个观察者的情况,只允许第一个观察者消费,而后续的观察者无法消费实现,这一般是不能满足需求的。
open class Event(private val content: T)
SingeLiveData 是 Google 官方的方案,在 LiveData 内部通过一个原子标志位来标记事件是否已经被消费过。这个方法本质上和 Event 实现包装器是一样的,因此也存在完全相同的副作用。
SingleLiveEvent.java
public class SingleLiveEvent extends MutableLiveData {
private static final String TAG = "SingleLiveEvent";
// 消费标记位
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(LifecycleOwner owner, final Observer observer) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, new Observer() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
业界分享出来的一个方案,不确定思路原创源。实现方法是在注册新观察者时,通过反射的手段将观察者持有的版本号(Observer#mLastVersion)同步为 LiveData 的版本号。缺点是使用反射,但确实能够解决多观察者问题。
private void hook(@NonNull Observer observer) throws Exception {
//get wrapper's version
Class classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
UnPeekLiveData 是 KunMinX 提出并开源的方案,主要思路是将 LiveData 源码中的 Observer#mLastVersion 和 LiveData#mVersion 在子类中重新实现一遍。在 UnPeekLiveData 中会有一个原子整型来标记数据版本,并且每个 Observer 在注册时会拿到当前 LiveData 的最新数据版本,而在 Observer#onChanged 中会对比两个版本号来决定是否分发。这个过程中没有使用反射,也不会存在不支持多观察者的问题。
ProtectedUnPeekLiveData.java
public class ProtectedUnPeekLiveData extends LiveData {
private final static int START_VERSION = -1;
private final AtomicInteger mCurrentVersion = new AtomicInteger(START_VERSION);
protected boolean isAllowNullValue;
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
super.observe(owner, createObserverWrapper(observer, mCurrentVersion.get()));
}
@Override
public void observeForever(@NonNull Observer super T> observer) {
super.observeForever(createObserverWrapper(observer, mCurrentVersion.get()));
}
public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
super.observe(owner, createObserverWrapper(observer, START_VERSION));
}
public void observeStickyForever(@NonNull Observer super T> observer) {
super.observeForever(createObserverWrapper(observer, START_VERSION));
}
@Override
protected void setValue(T value) {
mCurrentVersion.getAndIncrement();
super.setValue(value);
}
class ObserverWrapper implements Observer {
private final Observer super T> mObserver;
private int mVersion = START_VERSION;
public ObserverWrapper(@NonNull Observer super T> observer, int version) {
this.mObserver = observer;
this.mVersion = version;
}
@Override
public void onChanged(T t) {
if (mCurrentVersion.get() > mVersion && (t != null || isAllowNullValue)) {
mObserver.onChanged(t);
}
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ObserverWrapper that = (ObserverWrapper) o;
return Objects.equals(mObserver, that.mObserver);
}
@Override
public int hashCode() {
return Objects.hash(mObserver);
}
}
@Override
public void removeObserver(@NonNull Observer super T> observer) {
if (observer.getClass().isAssignableFrom(ObserverWrapper.class)) {
super.removeObserver(observer);
} else {
super.removeObserver(createObserverWrapper(observer, START_VERSION));
}
}
private ObserverWrapper createObserverWrapper(@NonNull Observer super T> observer, int version) {
return new ObserverWrapper(observer, version);
}
public void clear() {
super.setValue(null);
}
}
UnPeekLiveData.java
public class UnPeekLiveData extends ProtectedUnPeekLiveData {
@Override
public void setValue(T value) {
super.setValue(value);
}
@Override
public void postValue(T value) {
super.postValue(value);
}
public static class Builder {
private boolean isAllowNullValue;
public Builder setAllowNullValue(boolean allowNullValue) {
this.isAllowNullValue = allowNullValue;
return this;
}
public UnPeekLiveData create() {
UnPeekLiveData liveData = new UnPeekLiveData<>();
liveData.isAllowNullValue = this.isAllowNullValue;
return liveData;
}
}
}
Google 对 Flow 的定位是 Kotlin 环境下对 LiveData 的替代品,使用 SharedFlow 可以控制重放数量,可以设置为 0 表示禁止重放。
如果我们把事件理解为一种数据,LiveData 可以推数据自然也可以推事件,于是有人将 LiveData 封装为 “广播”,从而实现 “事件发送者” 和 “事件观察者” 的代码解耦,例如美团版本的 LiveDataBus。相较于 EventBus,LiveDataBus 实现更强的生命周期安全;相较于接口,LiveData 的约束力更弱。
无论是 EventBus 还是 LiveDataBus,它们本质上都是 “多对多的广播”,它们仅适合作为全局的事件通信,而页面内的事件通信应该继续采用 ViewModel + LiveData 等方案。这是因为事件总线缺乏 MVVM 模式建立的唯一可信源约束,事件发出后很难定位是哪个消息源推送出来的。
LiveDataBus 代码不多,核心在于使用哈希表保存事件名到 LiveData 的映射关系:
LiveDataBus.java
public final class LiveDataBus {
// 事件名 - LiveData 哈希表
private final Map> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
// 全局单例模式
private static class SingletonHolder {
private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DEFAULT_BUS;
}
// 根据事件名映射 LiveData
public MutableLiveData with(String key, Class type) {
if (!bus.containsKey(key)) {
// 构造新的 LiveData 对象
bus.put(key, new BusMutableLiveData<>());
}
return (MutableLiveData) bus.get(key);
}
// 根据事件名映射 LiveData
public MutableLiveData
使用 LiveDataBus:
LiveDataBus.get().with("key_test").setValue("");
LiveDataBus.get()
.with("key_test", String.class)
.observe(this, new Observer() {
@Override
public void onChanged(@Nullable String s) {
}
});
无论是 EventBus 还是 LiveDataBus 都没有对事件定义进行约束,不同开发者 / 不同组件可能会定义相同的事件字符串而导致冲突。
为了优化这个问题,可以使用美团 ModularEventBus 方案:用接口定义事件来实现强约束,在动态代理中取 接口名_方法名
作为事件名,再完成后续 LiveDataBus 的交互。
LiveDataBus.java
class LiveDataBus {
fun of(clz: Class): E {
if(!clz.isInterface){
throw IllegalArgumentException("API declarations must be interfaces.")
}
if(0 < clz.interfaces.size){
throw IllegalArgumentException("API interfaces must not extend other interfaces.")
}
return Proxy.newProxyInstance(clz.classLoader, arrayOf(clz), InvocationHandler { _, method, _->
// 取“接口名_方法名”作为事件名,再转交给 LiveDataBus
return@InvocationHandler get().with(
"${clz.canonicalName}_${method.name}",
(method.genericReturnType as ParameterizedType).actualTypeArguments[0].javaClass)
}) as E
}
}
另外,事件接口可以交给 APT 注解处理器生成:通过 DemoEvent 定义事件名常量,用 APT 将事件名转换为事件接口的方法:
DemoEvent.java
//可以指定module,若不指定,则使用包名作为module名
@ModuleEvents()
public class DemoEvents {
//不指定消息类型,那么消息的类型默认为Object
public static final String EVENT1 = "event1";
//指定消息类型为自定义Bean
@EventType(TestEventBean.class)
public static final String EVENT2 = "event2";
//指定消息类型为java原生类型
@EventType(String.class)
public static final String EVENT3 = "event3";
}
EventsDefineOfDemoEvents.java
package com.sankuai.erp.modularevent.generated.com.meituan.jeremy.module_b_export;
public interface EventsDefineOfDemoEvents extends com.sankuai.erp.modularevent.base.IEventsDefine {
com.sankuai.erp.modularevent.Observable EVENT1();
com.sankuai.erp.modularevent.Observable EVENT2(
);
com.sankuai.erp.modularevent.Observable EVENT3();
}
使用:
LiveDataBus
.get()
.of(EventsDefineOfDemoEvents::class.java)
.EVENT1()
.post(true)
LiveDataBus
.get()
.of(EventsDefineOfDemoEvents::class.java)
.EVENT1()
.observe(this, Observer {
Log.i(LOG, it.toString())
})
到这里,Jetpack 中的 LiveData 组件就讲完了,由于美团的 ModularEventBus 并没有开源,下篇文章我们直接来做一次学习落地。关注我,带你了解更多。
你的点赞对我意义重大!微信搜索公众号 [彭旭锐],希望大家可以一起讨论技术,找到志同道合的朋友,我们下次见!