2023-01-26
发布文章
使用或者设计某个应用架构的目的是什么?
简单的概括来说,是为了满足开闭原则,在不修改原有代码的情况下给程序扩展功能,而不是直接修改原有代码。
最终的目的是为了 提升开发测试效率,降低程序维护成本(降本增效)
基于上面的认知,我们选择使用一些其他的设计来实现开闭原则的目的。包括但不限于:单一职责,抽象接口,继承多态、解耦等等方式。
Android官方推荐的MVVM应用架构(现在已经不是官方最推荐架构了),主要是通过拆分View层(Activity/Fragmet)职责,简化View层的逻辑,分离View层和Model层之间的耦合的方式来实现开闭原则的目的
MVVM架构模型如下(每个组件仅依赖下一级组件):
这里需要强调的是:
尽管该架构本身是实现程序开闭原则的比较好实践之一,但是并不是所有的应用界面代码设计必须使用该架构。
应用架构的选择应该根据实际情况选择,没有最好的架构,仅仅只有当前场景的最优选择
下面主要分析下该架构下最重要的两个架构组件ViewModel和LiveData的使用和原理
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 by lazy {
MutableLiveData()
}
init {
// 请求返回字符串并保存
requestNetData(object : IHttpCallBack {
override fun onError(error: IHttpCallBack.HttpError?) {
pageData.value = null
}
override fun onEnd(data: String) {
pageData.value = data
}
})
说明:
LiveData 是一种可观察的数据存储器。应用中的其他组件可以使用此存储器监控对象的更改,而无需在它们之间创建明确且严格的依赖路径。LiveData 组件还能感知应用组件(如 Activity、Fragment 和 Service)的生命周期状态,能够自动清理观察者以防止对象泄漏和过多的内存消耗。
@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绑定应用组件的生命周期
//内部类
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)
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);
}
}
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, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//通知观察者数据改变分发(详见3.1.5)
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
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说明
setValue或者postValue(区别是postValue支持异步调用,最终还是会切换到主线程调用setValue方法)
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
//数据改变,version加一
mVersion++;
mData = value;
//调用3.1.4的分发数据方法,不过这里的观察者包装类对象为null因此会循环集合中所有的观察者对象通知数据更新
dispatchingValue(null);
}
ViewModel 对象为特定的界面组件(如 Fragment 或 Activity)提供数据,并包含数据处理业务逻辑,以与模型进行通信。例如,ViewModel 可以调用其他组件来加载数据,还可以转发用户请求来修改数据。ViewModel无法感知界面组件,因此不受配置更改(如在旋转设备时重新创建 Activity)的影响
ViewModel是如何保证同一个Activity对象仅有一个ViewMode对象?
ViewModel对象创建是通过ViewModelProvider对象的get方法来进行创建的,通过ViewModelProvider对象来保证ViewModel的唯一
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;
下列代码已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();
}
}
}
public class ViewModelStore {
private final HashMap 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 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对象
@NonNull
@MainThread
public T get(@NonNull Class 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 get(@NonNull String key, @NonNull Class 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;
}
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 create(@NonNull Class 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的唯一性
如何保证配置状态改变导致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;
}
1、 ViewModel不能持有Activity/Fragment/View相关对象的引用,因为ViewModel生命周期和组件的生命周期不一致,这种情况下可能(少数情况,因为一般我们不允许配置更新导致Activity重建,但是即使仅从设计模式的角度考虑我们也应该这么做)会导致内存泄露。
2、 LiveData.observer方法注册观察者的时候,注意如果是Fragment和View的情况。Fragment建议传递Fragment自身作为LifecycleOwner(和Activity的LifecycleOwner不是同一个)因为Fragment可能会先于 activity销毁。
3、组件真正销毁时,会调用ViewModel的onCleared方法,注意销毁必要的数据,比如说数据层的监听回调等等(或者在数据层使用弱应用,不是必须,需要注意数据层生命周期很长的情况,例如数据层是个单例,一直持有ViewModel的引用会导致ViewModel无法回收)
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