Android Jetpack MVVM数据绑定及UI更新
一.Jetpack简介
2018年谷歌I/O大会发布了一系列辅助Android开发者的实用工具,合称Jetpack,以帮助开发者构建出色的 Android 应用。
按照Google官方的说法,“Jetpack是一套库、工具和指南,可以帮助开发者更轻松地编写应用程序。Jetpack中的组件可以帮助开发者遵循最佳做法、摆脱编写样板代码的工作并简化复杂的任务,以便他们能将精力集中放在业务所需的代码上”。
Jetpack与AndroidX
在2018年的Google I/O大会上,Google宣布用AndroidX代替Android Support Library,AndroidSupport Library在版本28之后就不再更新了,未来的更新会在AndroidX中进行。不仅如此,AAC(Android Architecture Component)中的组件也被并入AndroidX。所以,当使用Jetpack的组件时,经常会看到以“androidx”开头的包名,Android Support Library与AAC中的各种组件已经迁移到了AndroidX中。
为什么Jetpack组件需要以兼容包的形式存在,而不是成为Framework的一部分呢?很简单,这是为了提供向后兼容,使Jetpack组件能够应对更加频繁的更新。除了Android Support Library和AAC,其他一些需要频繁更新和迭代的特性也被并入了AndroidX。
我们平常使用的DataBinding,LifeCycle,ViewModel,LiveData等都是Jetpack旗下的,之前也做过相应的分析:
Android Jetpack ViewModel详解
Android Jetpack Lifecycle详解
Android Jetpack LiveData原理分析
Android JetPack DataBinding分析
平常在开发的时候,都是会将其一起使用,比如:MVVM架构,使用DataBinding+LiveData+LifeCycle+ViewModel来进行实现,各司其职,将MVVM架构脱颖而出,本文通过一个MVVM实例来分析一下数据绑定及UI更新的实现原理。
二.MVVM实例
结合MVVM实例来分析一下ViewModel在MVVM中起了什么作用,LiveData是如何与UI进行绑定及UI如何更新。
a.View
public class ViewModelLiveDataFragment extends BaseFragment {
private MainViewModel mMainViewModel;
@Override
public int getLayoutId() {
return R.layout.livedata_layout;
}
@Override
public void initData(View view) {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LivedataLayoutBinding binding = DataBindingUtil.inflate(inflater, R.layout.livedata_layout, container, false);
View view = binding.getRoot();
mMainViewModel = new ViewModelProvider(this.getViewModelStore(), new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);
binding.setViewModel(mMainViewModel);
//通过以下逻辑来保证MutableLiveData变化时来更新UI
//该方法中最终会调用到observe()方法
binding.setLifecycleOwner(this);
return view;
}
}
以上可以看到,在onCreateView()时,去new一个ViewModelProvider(),然后通过来get()传入类来获取MainViewModel,用来存储数据。
b.ViewModel
public class MainViewModel extends ViewModel {
private ImageDepository mImageDepository;
public MainViewModel() {
mImageDepository = new ImageDepository();
step.setValue(1);
getImage(step.getValue());
}
public MutableLiveData<Integer> step = new MutableLiveData<>();
public MutableLiveData<String> imageUrl = new MutableLiveData<>();
public MutableLiveData<String> imageDescription = new MutableLiveData<>();
public void onClick(View view) {
if (view.getId() == R.id.up) {
step.setValue(step.getValue() - 1);
} else if (view.getId() == R.id.down) {
step.setValue(step.getValue() + 1);
}
getImage(step.getValue());
}
private void getImage(int step) {
Observable<ImageBean> observable = mImageDepository.getImage("js", step, 1);
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ImageBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ImageBean imageBean) {
List<ImageBean.ImagesBean> imagesBeans = imageBean.getImages();
ImageBean.ImagesBean imagesBean = imagesBeans.get(0);
String url = ImageBean.ImagesBean.BASE_URL + imagesBean.getUrl();
String des = imagesBean.getCopyright();
imageUrl.setValue(url);
imageDescription.setValue(des);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
以上可以看到,MainViewModel内部定义了许多LiveData变量,如果MainViewModel是唯一的,那么LiveData也就被存储下来,当UI从后台处于前台时,可以将最新值同步更新到UI。
c.M
public class ImageDepository {
private RetrofitApi mRetrofitApi;
public ImageDepository() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://cn.bing.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
mRetrofitApi = retrofit.create(RetrofitApi.class);
}
public Observable<ImageBean> getImage(String format, int idx, int n) {
return mRetrofitApi.getImage(format, idx, n);
}
}
M就是数据模块,用来获取数据,比如:用Retrofit请求网络数据等。
MVVM中,跟UI绑定的是LiveData,接下来看一下LiveData是如何与UI进行绑定的。
三.LiveData与UI进行绑定
在工程的generated目录下会生成xxBindingImpl.java文件,该文件实现xxViewDataBinding类,xxViewDataBinding类实现ViewDataBinding类。
public class LivedataLayoutBindingImpl extends LivedataLayoutBinding {
private LivedataLayoutBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
//调用父类的构造方法
super(bindingComponent, root, 3
, (android.widget.TextView) bindings[1]
, (android.widget.TextView) bindings[4]
, (android.widget.TextView) bindings[3]
);
this.description.setTag(null);
this.down.setTag(null);
this.mboundView0 = (android.widget.RelativeLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView2 = (android.widget.ImageView) bindings[2];
this.mboundView2.setTag(null);
this.up.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x10L;
}
requestRebind();
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
......
}
@Override
protected void executeBindings() {
........
........
// batch finished
if ((dirtyFlags & 0x1cL) != 0) {
// api target 1
//更新UI
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.description, viewModelImageDescriptionGetValue);
}
......
......
}
MutableLiveData是如何和UI进行绑定的?
1.xxBindingImpl类的构造方法,会最终调用ViewDataBinding类的构造方法,会根据MutableLiveData的数量来初始化创建对应数量的WeakListener,后续LiveData value变化会通知UI更新。
2.在xxBindingImpl构造方法中,执行了以下调用逻辑:
------>invalidateAll()
------>requestRebind()
------>.......
----->executePendingBindings()
------>executeBindingsInternal()
------>executeBindings(),
在executeBindings()里面来注册及更新UI。
3.在executeBindings()里面有一个updateLiveDataRegistration(0, viewModelImageUrl),此方法作为注册回调的入口,将对应的LiveData进行observe(),调用关系为:
------>updateRegistration(0, viewModelImageUrl, CREATE_LIVE_DATA_LISTENER)
------>registerTo()
------>listener.setTarget(viewModelImageUrl);
在registerTo()方法里面,获取到对应Observable[LiveData]的weakListener,然后将其赋值给步骤1创建的WeakListener数组里对应的值。
/**
* Method object extracted out to attach a listener to a bound LiveData object.
*/
private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new LiveDataListener(viewDataBinding, localFieldId).getListener();
}
};
private static class LiveDataListener implements Observer,
ObservableReference<LiveData<?>> {
final WeakListener<LiveData<?>> mListener;
LifecycleOwner mLifecycleOwner;
public LiveDataListener(ViewDataBinding binder, int localFieldId) {
//创建WeakListener时把自己作为Observable传进去,后续执行在WeakListener中执行setLifecycleOwner会用到
mListener = new WeakListener(binder, localFieldId, this);
}
@Override
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
LifecycleOwner owner = (LifecycleOwner) lifecycleOwner;
LiveData<?> liveData = mListener.getTarget();
if (liveData != null) {
if (mLifecycleOwner != null) {
liveData.removeObserver(this);
}
if (lifecycleOwner != null) {
//执行observe,主要做了两项操作:
//1.自身监听liveData的变化,有变化执行onChanged();
//2.liveData监听LifeCyclerOwner生命周期状态的变化
liveData.observe(owner, this);
}
}
mLifecycleOwner = owner;
}
@Override
public WeakListener<LiveData<?>> getListener() {
return mListener;
}
@Override
public void addListener(LiveData<?> target) {
if (mLifecycleOwner != null) {
target.observe(mLifecycleOwner, this);
}
}
@Override
public void removeListener(LiveData<?> target) {
target.removeObserver(this);
}
//mutableLiveData变化时会回调该方法
@Override
public void onChanged(@Nullable Object o) {
ViewDataBinding binder = mListener.getBinder();
if (binder != null) {
binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
}
}
}
4.后续会执行binding.setLifecycleOwner(this),在该方法中将在步骤1中创建的WeakListener分别执行setLifecycleOwner(this),会执行LiveDataListener.setsetLifecycleOwner(this)------>liveData.observe(owner, this),此方法主要做了两个操作:
①.MutableLiveData建立了对LifecycleOwner(activity/Fragment)生命周期的变化监听;
②.建立了对MutableLiveData的值变化监听;
总的执行逻辑图如下:
image.png
以上已经分析了LiveData是如何与UI进行绑定的,接下来看一下当LiveData发生变化后,是如何通知UI进行更新的。
四.LiveData如何通知UI更新
MutableLiveData变化后如何进行UI更新?
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
通过setValue()来对数据进行更新,先确保是主线程;然后将mVersion++,此处操作是为了避免重复发送更新;其次将value值进行存储在mData中,后续owner的生命周期变为active后,会利用到mData进行更新;最后执行dispatchingValue()进行实时更新。接下来看一下dispatchingValue()的实现:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
当initiator不为null时,是单独更新的,主要适应于owner生命周期状态变化时进行通知,由于传入的initiator为null,那么会执行else逻辑,遍历mObservers来进行通知observer进行更新UI,即调用到considerNotify(),看一下considerNotify()的实现:
private void considerNotify(ObserverWrapper observer) {
//如果observer不处于active,则不去更新,从而不会造成内存泄露
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//回调observer的onChanged()更新UI
observer.mObserver.onChanged((T) mData);
}
首先判断observer是否为active,否则直接返回;然后判断mLastVersion >= mVersion,避免重复更新,每次更新后都进行赋值;最后执行observer的onChanged()来进行回调更新。
简单总结一下:
1.MutableLiveData进行setValue()后,执行的调用关系如下:
------>dispatchingValue(null)
----->considerNotify()
------>observerWrapper.mObserver.onChanged((T) mData);
------>LiveDataListener.onChanged()
2.根据上述绑定UI的第4步observe()时传入的observer,会回调LiveDataListener里面的onChanged()方法。
3.onChanged()方法中会调用handleFieldChange(),会先调用xxBindingImpl中的onFieldChange()方法,然后调用requestRebind()------>.....------>调用xxBindingImpl中的executeBindings()来更新UI,流程图如下:
以上就是针对一个MVVM实例,对内部的数据绑定及UI更新进行了详细的分析,了解一个架构的实现原理,只能通过看源码!