1-MVVM简介
1.1-MVC & MVP & MVVM
MVP
MVVM与MVP结构类似,MVP也是通过Presenter将View与Model解耦。不过MVVM是基于观察者模式,viewModel不持有Activity/Fragment实例,数据更新驱动UI更新。
MVC
视图层用xml或者代码描述,控制层由Activity和Fragment实现。控制层太复杂,显示逻辑和其他逻辑在一起不便维护
MVVM
低耦合。View模块可独立于Model的变化。
复用性。一个ViewModel可以绑定到不同的View上,例如Activty与其子Fragment之间共享一个ViewModel,从而实现各View模块的数据共享和通信。
高内聚。ViewModel内聚业务逻辑和数据。View模块只含视觉相关。
1.2-MVVM结构
Model
Model层表示用户程序的数据和业务逻辑,这一层的的推荐的实现策略之一就是观测数据的变化并传递出去(供谁使用),使其从ViewModel或者其他观察者/消费者中完全解耦
ViewModel
ViewModel是和Model(数据层)进行交互,并且ViewMode可以被View观察.ViewModel可以选择性地为视图提供钩子以将事件传递给模型.该层的一个重要实现策略是将Model与View分离,即ViewModel不应该意识到与谁交互的视图
View
此模式中的视图角色是观察(或订阅)ViewModel,观察数据b变化,以便于获取数据去更新UI元素.
2-MVVM实现
在Android中实现MVVM模式,一般是通过DataBinding+ViewModel+LiveData。
DataBinding
是一个支持在xml中绑定数据的工具。通过在标签包裹xml布局,可以直接在xml中绑定数据或绑定事件。其实就是通过xml生成对应的DataBinding类,在里面完成了findview及数据绑定和事件绑定。
ViewModel
可感知订阅者LifeCycle,用于管理数据及数据分发。通过ViewModel可以实现Activity及其子Fragment的数据共享和通信
LiveData
可观察的数据对象,即数据变化时会通知订阅者。ViewModel中管理多个LiveData供订阅者订阅,且数据变化时双向的即订阅者可以被动接收数据也可以主动发送(改变)数据
来看下用DataBinding+LiveData+ViewModel的简单实现吧:
1.在build.gradle开启dataBinding。这样编译的时候就会通过APT将xml文件自动生成对应的Binding类
android {
dataBinding {
enabled = true
}
}
2.创建ViewModel
public class TestViewModel extends ViewModel {
//可观察数据
public MutableLiveData<String> userName = new MutableLiveData<>();
public String getUserName() {
return userName.getValue();
}
public void setUserName(String name) {
//setValue只能在主线程执行
//postValue可在子线程执行,通过主线程Handler切换到主线程执行setValue
userName.postValue(name);
}
}
3.创建布局xml及绑定数据
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable name="mViewModel" type="com.sxm.thirdframework.viewmodel.TestViewModel"/>
</data>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={mViewModel.userName}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</layout>
4.Activity中监听数据
public class MvvmTestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//ActivityMvvmBinding类是根据xml文件名自动生成的Binding类
ActivityMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
binding.setMViewModel(new ViewModelProvider(this).get(TestViewModel.class));
binding.setLifecycleOwner(this);//监听Activity的生命周期变化
//监听LiveData数据变化
binding.getMViewModel().userName.observe(this, testDo -> Log.d("MVVM_TEST", testDo));
}
}
这里也可以不用DataBinding,在Activity中自己实现View的数据绑定。
3-源码分析
3.1-DataBinding
先从最简单的DataBindign说起。DataBinding原理和ButterKnife类似,都是通过APT实现代码自动生成
(1)数据类注册。在2章中的xml文件经过编译后标签包裹的数据部分会注册到BR类,BR类类似R文件,注册后方便通过id索引对应的数据。看下xml中注册的viewModel在BR文件内容
<data>
<variable name="mViewModel" type="com.sxm.thirdframework.viewmodel.TestViewModel"/>
</data>
public class BR {
public static final int _all = 0;
public static final int mViewModel = 1;
}
(2)Binding类生成。而实现了标签的布局文件activity_mvvm.xml会在编译时生成对应的Binding类,类名为布局名去掉下滑线的驼峰式,这里就是ActivityMvvmBinding。ActivityMvvmBinding只是持有了设置了id的View的引用及绑定数据类的引用。具体实现类是ActivityMvvmBindingImpl,也是自动生成的。ActivityMvvmBindingImpl.executeBindings
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String mViewModelUserName = null;
com.sxm.thirdframework.viewmodel.TestViewModel mViewModel = mMViewModel;
if ((dirtyFlags & 0x3L) != 0) {
if (mViewModel != null) {
// read mViewModel.userName
mViewModelUserName = mViewModel.getUserName();
}
}
// batch finished
if ((dirtyFlags & 0x3L) != 0) {
//viewModel数据变化时通知EditText更新UI
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, mViewModelUserName);
}
if ((dirtyFlags & 0x2L) != 0) {
//监听EditText文本变更,并反向通知viewModel更新数据
//通过设置监听InverseBindingListener,来实现逆向数据更新
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView1, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView1androidTextAttrChanged);
}
}
(3)ID注册了怎么通过BR中的ID找到对应的数据呢?DataBinderMapper类也是在编译时自动生成的,具体实现类DataBinderMapperImpl。就是维护一个映射关系,可以通过layout文件的id来映射对应的Binding类。在Activity中的代码:
//调用activity的setContentView设置ContentView
//通过DataBinderMapperImpl根据layout的id获取对应的ActivityMvvmBinding对象,
ActivityMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
//绑定ViewModel对象
binding.setMViewModel(new ViewModelProvider(this).get(TestViewModel.class));
//绑定自身LifeCyle
binding.setLifecycleOwner(this);
//监听LiveData数据变化
binding.getMViewModel().userName.observe(this, testDo -> Log.d("MVVM_TEST", testDo));
3.2-ViewModel
ViewModel和LiveData都是JetPack中Lifecycle里的工具,较DataBinding出现晚一些。且ViewModel+LiveData可以不使用DataBinding实现MVVM模式,需要手动实现DataBinding完成的工作,这样的好处是业务逻辑不用在xml中实现,可读性及可维护性强。ViewModel具有以下特性:
1.ViewModel可以实现Activity与子Fragment之间的数据共享及通信,且不直接持有Activity/Fragment引用
2.ViewModel的生命周期和绑定Activity不同,Activity在onDestroy之前ViewModel都是活跃状态。所以Activity异常恢复重建时ViewModel中数据任然还在
3.ViewModel通过观察者的LifeCycle感知其生命周期,避免内存泄漏。
先看看ViewModel的创建过程:
new ViewModelProvider(this).get(TestViewModel.class)
第一步是创建ViewModelProvider,看下ViewModelProvider的构造方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
就是初始化了两个变量,一个是Factory,一个是ViewModelStore。Factory即ViewModel的工厂类,默认NewInstanceFactory。ViewModelStore维护了一个key是activity,value是viewModel的HashMap。构造方法中传入的this为Fragment/Activity。而Fragment及FragmentActivity
都实现了ViewModelStoreOwner接口。ViewModelStoreOwner接口只有一个方法ViewModelStore getViewModelStore()。
初始化了ViewModelProvider后get(TestViewModel.class)则通过类名反射构造一个TestViewModel实例,并存入ViewModelStore
到这里就能知道ViewModel的特性1了。由于ViewModelStore存储的是activity-viewModel。所以Activity及其子Fragment都实现了ViewModelStoreOwner接口,getViewModelStore()时都是从ViewModelStore获取的ViewModel实例,相同的Activity对应同一个实例。所以能实现数据共享和通信。
再来看看特性2是怎么实现的。
存储。当设备Configuration改变或其他原因导致Activity生命周期重建时,在onStop之后onDestory之前会调用ComponentActivity.onRetainNonConfigurationInstance将当前activity的viewModelStore封装成NonConfigurationInstances存储。
恢复。在Activity重建时onCreate方法通过getLastCustomNonConfigurationInstance获取NonConfigurationInstances对象从中拿到viewModelStore进而恢复View的状态。
销毁。那viewModel在什么时候销毁呢。看看ComponentActivity的构造方法就知道了。
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
。。。//省略
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
代码逻辑很清晰,就是通过监听Activity的Lifecycle,当进入Destroy前发送ON_DESTROY事件,调用getViewModelStore().clear()来销毁存储的viewModel。用一张图来概括:
3.3-Lifecycle
ViewModel的实现离不开Lifecycle,这里就来详细讲述下Lifecycle,看下怎么通过Lifecycle感知Activity/Fragment的生命周期,这里以ComponentActivity为例
首先ComponentActivity及Fragment都实现了LifecycleOwner接口。LifecycleOwner接口提供Lifecycle getLifecycle()方法
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
mLifecycleRegistry是封装了当前Activity实例弱引用,继承自Lifecycle
public LifecycleRegistry(@NonNull LifecycleOwner provider) {
mLifecycleOwner = new WeakReference<>(provider);
mState = INITIALIZED;
}
做完这些准备工作后,在ComponentActivity的onCreate方法中调用了
ReportFragment.injectIfNeededIn(this)。生命周期感知的核心就在这行代码了。来看源码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedStateRegistryController.performRestore(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
if (mContentLayoutId != 0) {
setContentView(mContentLayoutId);
}
}
ReportFragment.injectIfNeededIn
public static void injectIfNeededIn(Activity activity) {
if (Build.VERSION.SDK_INT >= 29) {
// @1.如果是android10及以上,直接通过LifecycleCallbacks监听Activity的生命周期变化
activity.registerActivityLifecycleCallbacks(
new LifecycleCallbacks());
}
// @2.在Android10以下,则是通过向Activity添加一个ReportFragment
// 通过ReportFragment来感知宿主Activity的生命周期变化
// 这样做的原则也是遵循对修改关闭对拓展开放,在不修改Activity源码的基础上就能感知其生命周期变化
android.app.FragmentManager manager = activity.getFragmentManager();
if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
manager.executePendingTransactions();
}
}
@1 Android 10以上直接通过registerActivityLifecycleCallbacks注册监听Activity的生命周期回调。注册监听后Activity的生命周期变化都会感知,例如Activity.onPause–>Activity.dispatchActivityPaused–>ActivityLifecycleCallbacks.onActivityPaused
@2 Android 10以下,则是通过向Activity添加一个ReportFragment。Activity生命周期变化时会联动ReportFragment生命周期变化。例如Activity.performPause–>FragmentController.dispatchPause–>FragmentManager.dispatchPause–>Fragment.performPause–>ReportFragment.onPause–>ReportFragment.dispatch。监听者就能收到ON_PAUSE事件
@Override
public void onPause() {
super.onPause();
dispatch(Lifecycle.Event.ON_PAUSE);
}
所以ViewModel的第3个特性,就是通过观察者的Lifecycle来判断是否处于活跃状态,只有活跃状态的观察者才会收到数据变更消息,非活跃状态的观察者将会从订阅列表移除。
3.4-LiveData
数据与View的双向通信则是通过LiveData来实现的。来看看是怎么订阅数据LiveData的:
binding.getMViewModel().userName.observe(this, testDo -> Log.d("MVVM_TEST", testDo));
1
调用LiveData的observe方法传入了LifecycleOwner 即Activity/Fragment,第二个参数是一个Observer的匿名类对象。observe方法只能在主线程,所以数据变化时的回调也是在主线程。
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");//断言主线程
//观察者处于非活跃状态不处理
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//将LifecycleOwner及Observer匿名类对象封装到LifecycleBoundObserver
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//以Oberver-LifecycleBoundObserver形式存入mObservers
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);
}
订阅其实就是将观察者及观察者回调接口匿名类加入到一个观察者列表mObservers。当调用LiveData.setValue时通知观察者,setValue只能在主线程。postValue是在子线程执行,通过主线程Handler切换到主线程调用setValue。这里主要分析setValue方法:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");//断言主线程
mVersion++;//数据变更次数
mData = value;//更新数据
dispatchingValue(null);//@3.通知订阅者
}
@3.通知订阅者
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
//核心方法,遍历观察者列表mObservers
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//@4.依次执行传入的回调接口匿名类的onChanged
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
//@4.依次执行传入的回调接口匿名类的onChanged
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//以最新的数据修改为准
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//执行回调接口方法
observer.mObserver.onChanged((T) mData);
}
最终在观察者的onChanged回调中收到了修改后的数据,可以看得出来如果两次setValue相同的数据,回调会执行两次。如果需要过滤重复数据,需要在onChanged回调中自行处理。