Android实现MVVM+LiveData+Retrofit
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- selected = new MutableLiveData
- (); public void select(Item item) { selected.setValue(item); } public LiveData
- 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()
@MainThreadpublic void observeForever(@NonNull Observer 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()
@MainThreadpublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer 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. * * 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 observer; public boolean active; public int lastVersion = START_VERSION; LifecycleBoundObserver(LifecycleOwner owner, Observer 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, 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()方法的具体实现:
@MainThreadprotected 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 userLiveData = ...; LiveData userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });
Transformations.switchMap()
与 map() 类似,对存储在 LiveData 对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap() 的函数必须返回 LiveData 对象,如以下示例中所示:
private LiveData getUser(String id) { ...; } LiveData userId = ...; LiveData user = Transformations.switchMap(userId, id -> getUser(id) );
更多相关文章
- Android里监视数据库的变化 registerContentObserver 接口
- Android下的数据储存方式(三)
- android中将数据写入手机内存和sdcard中的文件
- Android settings.db数据库中添加一条新的默认配置项
- Android之解析Json数据
- 《Android》Lesson08-Activity的生命周期
- Notification中Intent携带数据重复问题
- ThreadLocal原理解析(1):数据存取