Android架构组件使用和原理分析:ViewModel+LiveData
Posted datian1234
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android架构组件使用和原理分析:ViewModel+LiveData相关的知识,希望对你有一定的参考价值。
2023-01-26
发布文章
一、应用架构
1.1、设计目的
使用或者设计某个应用架构的目的是什么?
简单的概括来说,是为了满足开闭原则,在不修改原有代码的情况下给程序扩展功能,而不是直接修改原有代码。
最终的目的是为了 提升开发测试效率,降低程序维护成本(降本增效)
1.2、架构选择
基于上面的认知,我们选择使用一些其他的设计来实现开闭原则的目的。包括但不限于:单一职责,抽象接口,继承多态、解耦等等方式。
android官方推荐的MVVM应用架构(现在已经不是官方最推荐架构了),主要是通过拆分View层(Activity/Fragmet)职责,简化View层的逻辑,分离View层和Model层之间的耦合的方式来实现开闭原则的目的
MVVM架构模型如下(每个组件仅依赖下一级组件):
这里需要强调的是:
-
尽管该架构本身是实现程序开闭原则的比较好实践之一,但是并不是所有的应用界面代码设计必须使用该架构。
-
应用架构的选择应该根据实际情况选择,没有最好的架构,仅仅只有当前场景的最优选择
下面主要分析下该架构下最重要的两个架构组件ViewModel和LiveData的使用和原理
二、组件使用
简单使用示例(详细教程可查看如上官方教程):
ViewModel和LiveData一般组合使用(也可以单独使用,根据实际场景选择):
界面由MainActivity和对应的布局activity_main.xml组成。
首页需要请求页面数据,我们使用HomeViewModel请求和存储这些数据,定义了以下文件:
MainActivity
activity_main.xml
HomeViewModel
以下代码段显示了这些文件的起始内容(为简单起见,省略了布局文件)
MainActivity:
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Use the 'by viewModels()' Kotlin property delegate from the activity-ktx artifact
// or use ' ViewModelProvider(this).get(HomeViewModel::class.java)'
val viewModel: HomeViewModel by viewModels()
viewModel.pageData.observe(this, data ->
Log.d("mumu","data: $data")
)
HomeViewModel:
class HomeViewModel : ViewModel()
val pageData: MutableLiveData<String> by lazy
MutableLiveData<String>()
init
// 请求返回字符串并保存
requestNetData(object : IHttpCallBack
override fun onError(error: IHttpCallBack.HttpError?)
pageData.value = null
override fun onEnd(data: String)
pageData.value = data
)
说明:
- 在ViewModel层调用网络数据层的方法请求数据并保存到ViewModel的LiveData存储器中。
- 在View层(Activity)中注册观察者对象,观察LiveData的数据更新。无需在onStop停止观察,因为LiveData框架具备生命周期感知,检测观察者onStop了不会通知观察者数据更新。
三、组件原理
3.1、LiveData
LiveData 是一种可观察的数据存储器。应用中的其他组件可以使用此存储器监控对象的更改,而无需在它们之间创建明确且严格的依赖路径。LiveData 组件还能感知应用组件(如 Activity、Fragment 和 Service)的生命周期状态,能够自动清理观察者以防止对象泄漏和过多的内存消耗。
3.1.1、注册观察者
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
assertMainThread("observe");
//应用组件已经销毁,忽略
if (owner.getLifecycle().getCurrentState() == DESTROYED)
// ignore
return;
//将观察者对象封装成一个LifecycleBoundObserver对象(详见3.1.2)
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
//不为空说明已经在SafeIterableMap集合中了,同一个应用组件(Activity/Fragment)不能重复添加相同的观察者
if (existing != null && !existing.isAttachedTo(owner))
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
if (existing != null)
return;
//LifecycleBoundObserver实现LifecycleEventObserver接口,绑定Lifecycle的生命周期
owner.getLifecycle().addObserver(wrapper);
说明: 该步骤主要讲观察者对象observer封装成一个LifecycleBoundObserver对象,然后保存到集合中,并且通过LifeCycle绑定应用组件的生命周期
3.1.2、LifecycleBoundObserver观察者包装类
//内部类
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer)
super(observer);
mOwner = owner;
//判断观察者是否激活,应用组件生命周期处在onStart和onResume状态该方法才会返回true
@Override
boolean shouldBeActive()
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
//应用组件生命周期发生变化时回调该方法(Lifecycle组件的功能)
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event)
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
//判断应用组件已经destroy了则自动解注册
if (currentState == DESTROYED)
removeObserver(mObserver);
return;
Lifecycle.State prevState = null;
while (prevState != currentState)
prevState = currentState;
//状态方式改变,调用该方法改变LiveData状态和分发数据(详见3.1.3)
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
@Override
boolean isAttachedTo(LifecycleOwner owner)
return mOwner == owner;
@Override
void detachObserver()
mOwner.getLifecycle().removeObserver(this);
//LiveData的解注册方法(LiveData的方法)
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer)
assertMainThread("removeObserver");
//从集合中移除观察者包装对象
ObserverWrapper removed = mObservers.remove(observer);
//为空说明已经移除了
if (removed == null)
return;
//Lifecycle解注册
removed.detachObserver();
//调用activeStateChanged方法同步状态改变
removed.activeStateChanged(false)
3.1.3、activeStateChanged状态改变同步方法
void activeStateChanged(boolean newActive)
if (newActive == mActive)
return;
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
//该方法是通过加一减一来判断当前LiveData对象内是否有处在集合状态的观察者
//如果从无到有则调用onActive方法,从有到无则调用onInactive方法
//两个方法均为LiveData的空实现方法,我们可以做一些初始化或者数据重置的操作
changeActiveCounter(mActive ? 1 : -1);
if (mActive)
//真正开发分发数据的方法(只有观察者处于激活状态才会分发数据,详见3.1.4)
dispatchingValue(this);
3.1.4、dispatchingValue分发数据方法
void dispatchingValue(@Nullable ObserverWrapper initiator)
if (mDispatchingValue)
//mDispatchInvalidated标记数据是否失效,如果失效重新分发最新数据
mDispatchInvalidated = true;
return;
mDispatchingValue = true;
do
mDispatchInvalidated = false;
if (initiator != null)
//状态改变同步数据initiator不为空(首次注册情况下如果需要同步也是走到这里)
considerNotify(initiator);
initiator = null;
else
//更新数据情况同步所有观察者数据改变
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); )
//通知观察者数据改变分发(详见3.1.5)
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated)
break;
while (mDispatchInvalidated);
mDispatchingValue = false;
3.1.5、considerNotify通知观察者数据改变方法
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;
//判断数据是否已经通知了该观察者对象,防止重复更新(LiveData数据每更新一次,mVersion就会加一)
if (observer.mLastVersion >= mVersion)
return;
observer.mLastVersion = mVersion;
//没有通知过则调用观察者的onChanged方法通知数据更新
observer.mObserver.onChanged((T) mData);
说明:
走到这里,LiveData的一次完整数据观察和数据更新流程就结束了。
通过上述流程可知,LiveData会通过Lifecycle自动观测应用组件生命周期变化,组件销毁了自动解注册;组件激活了将观察者变为激活状态并及时同步数据给观察者。观察者只需要在数据更新方法中做相应的核心逻辑处理,而无需关心数据丢失或者数据重复回调,也无需担心内存泄露,LiveData会自动解注册观察者。
最后还有就是LiveData的数据更新方法(setValue和postValue方法),详见3.1.6说明
3.1.6、更新数据方法
setValue或者postValue(区别是postValue支持异步调用,最终还是会切换到主线程调用setValue方法)
@MainThread
protected void setValue(T value)
assertMainThread("setValue");
//数据改变,version加一
mVersion++;
mData = value;
//调用3.1.4的分发数据方法,不过这里的观察者包装类对象为null因此会循环集合中所有的观察者对象通知数据更新
dispatchingValue(null);
3.2、ViewModel
ViewModel 对象为特定的界面组件(如 Fragment 或 Activity)提供数据,并包含数据处理业务逻辑,以与模型进行通信。例如,ViewModel 可以调用其他组件来加载数据,还可以转发用户请求来修改数据。ViewModel无法感知界面组件,因此不受配置更改(如在旋转设备时重新创建 Activity)的影响
ViewModel是如何保证同一个Activity对象仅有一个ViewMode对象?
ViewModel对象创建是通过ViewModelProvider对象的get方法来进行创建的,通过ViewModelProvider对象来保证ViewModel的唯一
3.2.1、ViewModelProvider构造方法
ViewModelProvider共有三个构造方法,如下:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner)
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
//ViewModelStoreOwner是个接口只有一个方法getViewModelStore,由Activity/Fragment实现(详见3.2.2)
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory)
this(owner.getViewModelStore(), factory);
//最终会调用的该构造方法,ViewModelStore封装了HashMap集合保存ViewModel对象
//ViewModelStore详见3.2.3
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory)
mFactory = factory;
mViewModelStore = store;
3.2.2、实现ViewModelStoreOwner接口
下列代码已Activity为例,Fragment类似
@NonNull
@Override
public ViewModelStore getViewModelStore()
if (getApplication() == null)
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
ensureViewModelStore();
return mViewModelStore;
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore()
//mViewModelStore是ComponentActivity的成员变量,内部通过HashMap存储数据ViewModel
if (mViewModelStore == null)
//如果为空,先尝试从mLastNonConfigurationInstances中取,
//mLastNonConfigurationInstances是activity的成员变量,这个值在Acivity attach 的时候赋值
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null)
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
//如果取值为空则重新创建一个
if (mViewModelStore == null)
mViewModelStore = new ViewModelStore();
3.2.3、ViewModelStore类
public class ViewModelStore
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel)
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null)
oldViewModel.onCleared();
final ViewModel get(String key)
return mMap.get(key);
Set<String> keys()
return new HashSet<>(mMap.keySet());
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear()
for (ViewModel vm : mMap.values())
vm.clear();
mMap.clear();
说明: 其实就是个HashMap,用来保存ViewModel对象
3.2.4、ViewModelProvider.get(ViewModel.class)方法获取ViewModel实例
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass)
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null)
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass)
ViewModel viewModel = mViewModelStore.get(key);
//优先从ViewModelStore中获取,不为空则直接返回
if (modelClass.isInstance(viewModel))
if (mFactory instanceof OnRequeryFactory)
((OnRequeryFactory) mFactory).onRequery(viewModel);
return (T) viewModel;
else
//noinspection StatementWithEmptyBody
if (viewModel != null)
// TODO: log a warning.
//为空则通过mFactory创建,
//这里的mFactory如果ViewModeStoreOwner没有实现HasDefaultViewModelProviderFactory
//则默认使用NewInstanceFactory通过反射构建ViewModel对象。
//Componentactivity默认是实现了HasDefaultViewModelProviderFactory接口的,返回 SavedStateViewModelFactory,
//如果是ViewModel是无参构造则最后还是会通过NewInstanceFactory构建对象
if (mFactory instanceof KeyedFactory)
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
else
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
3.2.5、NewInstanceFactory工厂类
NewInstanceFactory通过反射构建ViewModel对象,方法比较简单,其他工厂类似
public static class NewInstanceFactory implements Factory
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance()
if (sInstance == null)
sInstance = new NewInstanceFactory();
return sInstance;
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass)
//noinspection TryWithIdenticalCatches
try
return modelClass.newInstance();
catch (InstantiationException e)
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
catch (IllegalAccessException e)
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
说明: 上述5个步骤即保证了ViewModel对象在Activity/Fragment的唯一性
3.2.6、配置改变导致Activity重建状态保存
如何保证配置状态改变导致Activity重建的情况下,ViewModel不变?
主要是靠NonConfigurationInstances对象,代码说明如下
@Nullable
public final Object onRetainNonConfigurationInstance()
Object custom = this.onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = this.mViewModelStore;
ComponentActivity.NonConfigurationInstances nci;
if (viewModelStore == null)
nci = (ComponentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
if (nci != null)
viewModelStore = nci.viewModelStore;
if (viewModelStore == null && custom == null)
return null;
else
nci = new ComponentActivity.NonConfigurationInstances();
nci.custom = custom;
//保存viewModelStore
nci.viewModelStore = viewModelStore;
return nci;
四、实践总结
4.1、内存泄露
1、 ViewModel不能持有Activity/Fragment/View相关对象的引用,因为ViewModel生命周期和组件的生命周期不一致,这种情况下可能(少数情况,因为一般我们不允许配置更新导致Activity重建,但是即使仅从设计模式的角度考虑我们也应该这么做)会导致内存泄露。
2、 LiveData.observer方法注册观察者的时候,注意如果是Fragment和View的情况。Fragment建议传递Fragment自身作为LifecycleOwner(和Activity的LifecycleOwner不是同一个)因为Fragment可能会先于 activity销毁。
3、组件真正销毁时,会调用ViewModel的onCleared方法,注意销毁必要的数据,比如说数据层的监听回调等等(或者在数据层使用弱应用,不是必须,需要注意数据层生命周期很长的情况,例如数据层是个单例,一直持有ViewModel的引用会导致ViewModel无法回收)
4.2、数据保存
1、 Activity/Fragment 或者Fragment/Frament之间传递数据,注意传递的ViewModelStore需要时同一个,因为Fragment也会实现ViewModelStoreOwner接口,通过Fragment的getViewModelStore方法和Activity的getViewModelStrore方法获取的ViewModelStore并不是同一个,所以最终获取到的ViewModel对象并不是同一个。Fragment共享数据需要相同的Activity作为ViewModelStoreOwner,建议使用requireActivity方法获取Acitivity。
2、 ViewModel只能在配置更新导致的Activity重建的情况下保存,例如屏幕旋转。并不能在异常销毁Activity的情况保存数据,例如操作系统因为内存紧张杀掉进程的情况,这种情况数据并不能通过ViewModel恢复数据,可以考虑使用onSaveInstanceState方法或者其他方法保存数据。
作者:精神小伙JUMP
链接:https://juejin.cn/post/7190193347229646885
以上是关于Android架构组件使用和原理分析:ViewModel+LiveData的主要内容,如果未能解决你的问题,请参考以下文章
Android Jetpack架构组件——Lifecycle原理篇
Android Jetpack架构组件——Lifecycle原理篇