1. MVVM 架构详细介绍及源码层面理解
整体架构
MVVM(Model - View - ViewModel)架构是为了解决视图和数据模型之间的耦合问题而设计的。它通过引入 ViewModel 作为中间层,实现了视图和数据的分离,提高了代码的可维护性和可测试性。
- View(视图层):在 Android 中,View 主要关联 Activity、Fragment 以及 XML 布局文件等,负责呈现界面与响应用户交互。像 Activity 里设置布局、初始化视图元素,以及处理用户点击等操作都属于 View 范畴。例如在一个登录界面 Activity 里,布局文件定义了输入框、按钮等 UI 元素的样式与位置,Activity 则处理按钮点击事件,这些都在 View 层完成。
- Model(数据模型层):负责数据的获取、存储与处理。比如从网络请求用户信息、将数据存储到本地数据库等。在电商应用中,从服务器获取商品列表数据,或是将用户的购物车信息保存到本地数据库,都由 Model 层执行。
- ViewModel(视图模型层):作为连接 View 与 Model 的纽带,承担关键的界面显示逻辑处理任务。它从 Model 获取数据,并将其转换为适合 View 展示的形式。例如在新闻应用中,Model 获取到原始新闻数据列表,ViewModel 可对数据进行加工,如截取新闻摘要、处理图片链接等,让数据能更好地在 View 中展示。在数据更新时,ViewModel 通过 LiveData 等机制通知 View 进行相应更新。从源码层面看,ViewModel 借助 ViewModelProvider 来创建与管理。ViewModelProvider 内部运用 ViewModelStore 存储 ViewModel 实例,确保配置变更(如屏幕旋转)时,ViewModel 实例不会被销毁,维持数据的稳定性。
观察者模式在 MVVM 中的应用:
在 MVVM 架构里,观察者模式发挥着核心作用。ViewModel 持有数据,以 LiveData 为例,View 作为观察者监听 LiveData 数据变化。LiveData 内部维护了观察者列表,当数据变更时,会调用 dispatchingValue
方法遍历观察者列表。在 considerNotify
方法中,判断观察者状态,若活跃则通过 observer.mObserver.onChanged((T) mData)
通知观察者,View 接收到通知后更新界面,实现了 View 与 ViewModel 低耦合通信,这在诸多面试真题里都有涉及,是理解 MVVM 架构的关键。ViewModel 是通过 ViewModelProvider
来创建和管理的。
2. LiveData 实例化方法及源码分析
实例化方法
- MutableLiveData :
MutableLiveData
是LiveData
的子类,它公开了setValue()
和postValue()
方法,允许外部修改其持有的数据。
java
MutableLiveData<String> liveData = new MutableLiveData<>();
在源码中,MutableLiveData
只是简单地继承了 LiveData
并暴露了修改数据的方法。
java
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
- 使用
Transformations
类进行转换 :Transformations
类提供了一些静态方法,如map()
和switchMap()
,可以对 LiveData 进行转换。
java
LiveData<Integer> source = new MutableLiveData<>();
LiveData<String> transformed = Transformations.map(source, input -> "Transformed: " + input);
map()
方法的源码实现如下:
java
public static <X, Y> LiveData<Y> map(
LiveData<X> source,
final Function<X, Y> mapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(mapFunction.apply(x));
}
});
return result;
}
3. LiveData 如何实现生命周期绑定问题
LiveData 生命周期绑定机制在面试中频繁被问到,其实现依赖于 Android 的 Lifecycle 组件。
当调用 LiveData.observe()
方法时,会创建 LifecycleBoundObserver
对象,它实现了 LifecycleEventObserver
接口来监听 LifecycleOwner
(如 Activity、Fragment)的生命周期变化。在 observe()
方法源码中,先检查 LifecycleOwner
状态,若已销毁则直接返回;否则创建 LifecycleBoundObserver
并添加到观察者列表,同时将其注册到 LifecycleOwner
的生命周期观察者中。
java
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
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
的 onStateChanged
方法会在 LifecycleOwner
生命周期状态变化时被调用。在此方法中,根据生命周期状态决定是否更新观察者。当状态变为 DESTROYED
时,从 LiveData 的观察者列表移除该观察者,防止内存泄漏;当状态为 STARTED
或 RESUMED
时,认为观察者活跃,可接收数据更新。
java
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
这种机制确保 LiveData 仅在 LifecycleOwner
处于活跃状态(STARTED
或 RESUMED
)时更新观察者,有效避免内存泄漏与空指针异常,是 LiveData 的重要特性。
4. LiveData 粘性事件的深入分析
粘性事件的概念
粘性事件是指当一个观察者注册到 LiveData 时,即使该 LiveData 在观察者注册之前已经有了更新,观察者仍然会接收到这些之前的更新。这是因为 LiveData 会记录最新的值,当有新的观察者注册时,会立即将最新的值发送给它。
源码层面分析
在 LiveData
的 observe()
方法中,当新的观察者注册时,会调用 dispatchingValue()
方法,该方法会检查观察者的状态,并将最新的值发送给它。
java
private 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;
}
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 调用观察者的 onChanged 方法,发送最新的值
observer.mObserver.onChanged((T) mData);
}
在 considerNotify()
方法中,会比较观察者的 mLastVersion
和 LiveData 的 mVersion
,如果 mLastVersion
小于 mVersion
,则会调用观察者的 onChanged()
方法,将最新的值发送给它。
解决粘性事件的方法
为了避免粘性事件的影响,可以考虑使用一些第三方库,如 SingleLiveEvent
或自定义 LiveData
实现。以下是一个简单的自定义 LiveData
实现,用于避免粘性事件:
java
import androidx.lifecycle.LiveData;
import java.util.concurrent.atomic.AtomicBoolean;
public class NonStickyLiveData<T> extends LiveData<T> {
private final AtomicBoolean mPending = new AtomicBoolean(false);
@Override
public void observeForever(@NonNull Observer<? super T> observer) {
super.observeForever(new Observer<T>() {
@Override
public void onChanged(T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@Override
protected void setValue(T value) {
mPending.set(true);
super.setValue(value);
}
@Override
protected void postValue(T value) {
mPending.set(true);
super.postValue(value);
}
}
在这个自定义的 LiveData
中,使用 AtomicBoolean
来标记是否有新的值需要发送,只有当 mPending
为 true
时,才会调用观察者的 onChanged()
方法,从而避免了粘性事件的影响。
粘性事件总结
在 LiveData 机制里,不活跃观察者(对应 LifecycleOwner
处于 STOPPED
或 PAUSED
状态)正常情况下不会接收数据更新事件。只有当观察者再次变为活跃状态时,LiveData 才会将最新数据发送给它。这是因为在 LifecycleBoundObserver
的 shouldBeActive
方法中,依据 LifecycleOwner
的当前生命周期状态判断观察者是否活跃,不活跃则不进行数据分发。
然而,LiveData 存在粘性事件问题,这在面试中常被提及。粘性事件指新观察者注册时,即便 LiveData 之前已有更新,观察者仍会收到这些之前的更新数据。从源码层面分析,在 LiveData
的 observe()
方法中,新观察者注册后会调用 dispatchingValue()
方法。在 dispatchingValue()
内部的 considerNotify()
方法里,通过比较观察者的 mLastVersion
和 LiveData 的 mVersion
来决定是否通知观察者。若 mLastVersion
小于 mVersion
,则调用观察者的 onChanged()
方法发送最新数据,导致粘性事件发生。
为解决粘性事件问题,常见方法如下:
- 使用
SingleLiveEvent
:自定义一个继承自MutableLiveData
的类,重写相关方法确保事件只被消费一次。例如在一些开源项目中,SingleLiveEvent
类通过设置标志位,在observe()
方法中判断标志位,仅在首次观察时触发数据更新,后续不再响应之前的粘性数据。 - 使用
Event
包装类 :将数据包装在Event
类中,通过标记数据是否已被处理来避免重复触发。在观察者获取数据时,先检查标记位,若未处理则处理数据并设置标记位,防止重复处理粘性数据。 - 使用
MediatorLiveData
:MediatorLiveData
可监听其他 LiveData 变化,并在必要时过滤粘性事件。通过添加源 LiveData 的观察者,在数据变化时进行相应处理,如更新自身数据后移除源 LiveData,避免粘性事件传递给新观察者。