Android实现MVVM
MVVM简介:
MVVM(Model-View-ViewModel) 是由Mvp演变而来。
-
View包含Ui布局,以及布局生命周期控制器(Activity,Fragment)
-
DataBinding实现view层与viewModel数据的双向绑定(但实际上在Android Jetpack中DataBinding只存在于布局和布局生命周期控制器之间,当数据变化绑定到布局生命周期控制器时再转发给ViewModel,布局控制器可以持有DataBinding但ViewModel不应该持有DataBinding)
-
ViewModel负责处理数据和实现业务逻辑,但是ViewModel层不应该直接或者间接地持有View层的任何引用,因为一个ViewModel不应该直达自己具体是和哪一个View进行交互的.ViewModel主要的工作就是将Model提供来的数据直接翻译成View层能够直接使用的数据,并将这些数据暴露出去,同时ViewModel也可以发布事件,供View层订阅.
ViewModel:
ViewModel是一个可以用来存储UI相关的数据的类。ViewModel的生命周期会比创建它的Activity、Fragment的生命周期长,禁止在ViewModel的持有Activity、Fragment、View等对象,这样会引起内存泄漏。如果需要在ViewModel中引用Context的话,google为我们提供了AndroidViewModel 类来供我们使用。
ViewModel优点:
- 不会因为屏幕旋转或者其他配置改变(比如切换到多窗口模式)而销毁,避免数据重新创建,比如网络数据重新加载的情况。
- 当Activity或者Fragment的正常销毁的时候,自动清除数据,也就是绑定了Activity或者Fragment的生命周期,避免了内存泄漏。
- 多个Fragment之间或者Activity和Fragment之间更好地传递数据,进行交互。
官方案例:在 Fragment 之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。想象一下主从 Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
可以使用 ViewModel 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), { item ->
// Update the UI.
});
}
}
**请注意:**这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。
此方法具有以下优势:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了 SharedViewModel 约定之外,Fragment不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
- 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个Fragment,界面将继续工作而没有任何问题。
ViewModel 和 onSaveInstanceState 区别
ViewModel
- 缓存在内存中的,相当于本地缓存和网络缓存读写比较快
- 可以存较大的值,比如bitmap、大对象等等
- 数据绑定了Activity或者Fragment的生命周期,当Activity正常关闭的时候,都会清除ViewModel中的数据.比如
| 调用finish()
|| 按返回键退出,
|| 用户直接杀进程或者是放后台内存不足,被回收了 - 在Activity旋转或者其他配置改变的时候,ViewModel里面是还有值得,不会销毁,
- ViewModel也可以使用本地存储,通过SavedStateHandle实现的,使用SavedStateViewModelFactory这个工厂,这里就不多介绍了,已经是正式版本了,引用是
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0
onSaveInstanceState
- 仅适合可以序列化再反序列化的少量数据
- 不适合数量可能较大的数据,如用数组或Bitmap。可以存一些简单的基本类型和简单的小对象、例如字符串,和一些id
- 会把数据存储到磁盘上
注意: 对于复杂的大型数据,请使用 数据库 或者 SharedPreferences
LiveData
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
特点:
- 数据可以被观察者订阅;
- 能够感知组件(Fragment、Activity、Service)的生命周期;
- 只有在组件出于激活状态(STARTED、RESUMED)才会通知观察者有数据更新;
使用LiveData的理由:
-
能够保证数据和UI统一,
这个和LiveData采用了观察者模式有关,LiveData是被观察者,当数据有变化时会通知观察者(UI)。
-
减少内存泄漏
这是因为LiveData能够感知到组件的生命周期,当组件处于DESTROYED状态时,观察者对象会被清除掉。
- 当Activity停止时不会引起崩溃
这是因为组件处于非激活状态时,不会收到LiveData中数据变化的通知。
- 不需要额外的手动处理来响应生命周期的变化
这一点同样是因为LiveData能够感知组件的生命周期,所以就完全不需要在代码中告诉LiveData组件的生命周期状态。
- 组件和数据相关的内容能实时更新
组件在前台的时候能够实时收到数据改变的通知,这是可以理解的。当组件从后台到前台来时,LiveData能够将最新的数据通知组件,这两点就保证了组件中和数据相关的内容能够实时更新。
- 针对configuration change时,不需要额外的处理来保存数据
我们知道,当你把数据存储在组件中时,当configuration change(比如语言、屏幕方向变化)时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。
- 资源共享
通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者,这个在继承LiveData中会看到具体的例子。
源码分析:
添加观察者
LiveData提供了两种添加观察者的方法:observeForever()、observe()。
- observeForever()
@MainThread
public void observeForever(@NonNull Observer<T> observer) {
observe(ALWAYS_ON, observer);
}
从方法的命名,我们也能对它的功能略知一二,通过observeForever()添加观察者,观察者会一直受到数据的变化回到,而不是在组件处于STARTED和RESUMED状态下才会收到,因为这是LifecycleOwner对象就不再是组件了,而是ALWAYS_ON;另外通过该方法添加观察者后,要手动调用removeObserver()方法来停止观察者接收回调通知。observeForever()方法体很简单,调用了observe()方法,传入的一个参数是ALWAYS_ON常量,重点看下ALWAYS_ON常量是个啥东东。
private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
private LifecycleRegistry mRegistry = init();
private LifecycleRegistry init() {
LifecycleRegistry registry = new LifecycleRegistry(this);
registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
return registry;
}
@Override
public Lifecycle getLifecycle() {
return mRegistry;
}
};
ALWAYS_ON是LifecycleOwner常量,在init方法中会初始化Lifecycle的生命周期状态,完了之后,就没有改变过Lifecycle的生命周期状态了,这也就是为什么通过observeForever()添加观察者是,当数据改变时不管组件处于什么状态都会收到回调的原因,除非手动将观察者移除。
- observe()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//将LifecycleOwner对象和Observer对象封装成LifecycleBoundObserver对象。
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// mObservers可以理解成一个类似Map的容器,putIfAbsent()方法是判断容器中的observer(key)
// 是否有已经和wrapper(value)关联,如果已经关联则返回关联值,否则关联并返回wrapper。
LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && existing.owner != wrapper.owner) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper); //条件LifecycleOwner的生命周期观察者
}
setValue
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* This method must be called from the main thread. If you need set a value from a background
* thread, you can use {@link #postValue(Object)}
*
* @param value The new value
*/
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
**dispatchingValue(null)**方法用于通知观察者
组件(Fragment/Activity)生命周期发生变化
在LiveData.observe()方法中添加了组件(实现了LifecycleOwner接口的Fragment和Activity)生命周期观察者。而这个观察者就是LifecycleBoundObserver对象,下面我们看下LifecycleBoundObserver具体实现。
class LifecycleBoundObserver implements GenericLifecycleObserver {
public final LifecycleOwner owner;
public final Observer<T> observer;
public boolean active;
public int lastVersion = START_VERSION;
LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
this.owner = owner;
this.observer = observer;
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// LifecycleOwner对象生命周期发生变化时,会通过该回调方法通知过来。
// 如果当前处于Lifecycle.State.DESTROYED时,会自动将观察者移除。
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(observer);
return;
}
// 判断是否处于actived状态,并将结果作为参数传递给activeStateChanged()
activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
}
void activeStateChanged(boolean newActive) {
if (newActive == active) { //新状态和之前状态相同,不处理
return;
}
active = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += active ? 1 : -1;
if (wasInactive && active) { //处于激活状态的observer个数从0到1
onActive();
}
if (LiveData.this.mActiveCount == 0 && !active) { //处于激活状态的observer个数从1变为0
onInactive();
}
if (active) {
dispatchingValue(this);
}
}
}
通过observe()方法添加观察者时,当组件(Fragment/Activity)生命周期发生变化时,onStateChanged()方法会被调用。如果通过observeForever()方法添加观察者时,只有在常量ALWAYS_ON初始化的时候,onStateChanged()方法会被调用三次(CREATED、STARTED、RESUMED),后面就不会收到DESTROYED的状态,这也就是为什么要通过removeObserver()方法手动移除观察者的原因。
onActive()和onInactive()都是空实现的方法,继承类可以选择去实现。我们看下dispatchingValue()方法的具体实现。
private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) { // initiator不为空,考虑通知回调
considerNotify(initiator);
initiator = null;
} else { // initiator为空,考虑通知mObservers容器中对象回调
for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
private void considerNotify(LifecycleBoundObserver observer) {
if (!observer.active) {
return;
}
if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
observer.activeStateChanged(false);
return;
}
if (observer.lastVersion >= mVersion) {
return;
}
observer.lastVersion = mVersion;
// 最终回调的地方,也就是调用observe()或者observeForever()是传入的Observer对象。
observer.observer.onChanged((T) mData);
}
改变LiveData数据
LiveData提供了两种改变数据的方法:setValue()和postValue()。区别是setValue()要在主线程中调用,而postValue()既可在主线程也可在子线程中调用。我们先看setValue()方法的具体实现:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue"); //判断当前线程是否是主线程,不是主线程就抛出异常
mVersion++;
mData = value;
dispatchingValue(null);
}
再看下postValue()方法的具体实现:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 会在主线程中执行 mPostValueRunnable中的内容。
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
// 在主线程中调用setValue()方法
setValue((T) newValue);
}
};
postValue()方法通过ArchTaskExecutor实现在主线程中执行mPostValueRunnable对象中的内容,而在mPostValueRunnable中最终会调用setValue()方法来实现改变LiveData存储的数据。
转换 LiveData
您可能希望在将 LiveData 对象分派给观察者之前对存储在其中的值进行更改,或者您可能需要根据另一个实例的值返回不同的 LiveData 实例。Lifecycle 软件包会提供 Transformations 类,该类包括可应对这些情况的辅助程序方法。
Transformations.map()
对存储在 LiveData 对象中的值应用函数,并将结果传播到下游。
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
与 map() 类似,对存储在 LiveData 对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap() 的函数必须返回 LiveData 对象,如以下示例中所示:
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );