Android-DataBinding原理分析
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android-DataBinding原理分析相关的知识,希望对你有一定的参考价值。
参考技术A在MainActivity中,调用下面的方法:
app\\build\\intermediates\\data_binding_layout_info_type_merge\\debug\\out
可以看到,这里定义了多个Target标签,这些Target的定义,其实就是定义对应的tag,将tag与activity_main.xml布局中的对应的View的id对应起来
经过DataBinding变化后的布局,会多出tag。
app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml
其实DataBindingUtil的setContentView()方法,主要就是调用activity的setContentView设置布局,并且绑定添加对应的View
这里的sMapper是一个DataBinderMapper对象,其实现类是DataBinderMapperImpl
DataBinderMapperImpl是通过apt注解处理器生成的。
这里的sMapper.getDataBinder()其实就是调用的MergedDataBinderMapper的getDataBinder()方法
而sMapper中的数据,其实就是DataBinderMapperImpl的构造器中调用其父类MergedDataBinderMapper 的addMapper()方法添加的对象
在DataBinding中有两个DataBinderMapperImpl类,一个是上面这个在androidx.databinding包下,继承了MergedDataBinderMapper的,另一个是在com.example.databindingdemo应用包下,直接继承DataBinderMapper。其实MergedDataBinderMapper也是继承自DataBinderMapper
这里要注意两点,就是如果是布局的顶层View,比如tag为layout/activity_main_0,那么就会new一个ActivityMainBindingImpl对象。这个tag,其实可以从前面看到的app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml布局中的LinearLayout的tag知道
在new出ActivityMainBindingImpl对象后,则进行一些View的绑定操作,将通过tag取出的View与ActivityMainBindingImpl中对应的View属性进行绑定。
在这里,会调用了一个mapBindings方法,第三个参数是一个3,这个3的意思,就是activity_main.xml布局文件中有3个节点
mapBindings就会返回一个Object[] bindings数组。
这里的主要工作,就是将布局中的View保存在对应的bindings数组中,然后取出这个数组中的数据赋值给ActivityMainBindingImpl中的View
ActivityMainBindingImpl的父类ActivityMainBinding是在Eapp\\build\\generated\\data_binding_base_class_source_out\\debug\\out\\com\\example\\databindingdemo\\databinding包下
BR的作用: 其实BR的作用,就用BR中的属性值来标记不同的操作需要的监听在mLocalFieldObservers数组中的位置
这里的localFieldId=0,这个id其实就BR文件中的id,就是BR文件中对应的静态final属性的值。而第二个就是观察者对象,比如传入的ViewModel对象。
这里通过WeakListener监听器中的ObservableReference对象保存观察者与被观察者,当被观察者发生改变的时候,就会找到对应的WeakListener监听器,然后通知观察者做修改。
而ObservableReference方法的实现,有多个,比如:WeakPropertyListener。
这里让WeakListener.setTarget()其实就是通过WeakPropertyListener给被观察者添加callback,然后当被观察者数据发生改变的时候,被观察者通过遍历其内部的PropertyChangeRegistry中的OnPropertyChangedCallback回调(其实就是WeakPropertyListener),然后通过WeakPropertyListener监听通知给ViewDataBinding以及其实现类ActivityMainBindingImpl具体进行数据的处理和设置。
// 这里的mTarget其实是一个泛型T对象,而这个泛型是在WeakPropertyListener初始化WeakListener的时候传入的一个Observable,这个是databinding中的Observable,其子类实现就是BaseObservable
WeakPropertyListener中的addListener方法,就会给Observable添加一个callback回调,向Observable这个被观察者中添加callback的目的,就是在Observable数据发生变化的时候,遍历Observable中的mCallbacks这个callback集合,通知观察者进行修改。
从这第三步可以知道:
而WeakPropertyListener和WeakListener是相互持有的对方的引用。
在完成监听的相互绑定关系,并且给Observable添加了回调之后,就会回到ActivityMainBindingImpl的setUser()方法继续执行notifyPropertyChanged()方法。
但是这里的例子有个问题,就是监听是添加在User这个BaseObservable的子类中的,但是更新的时候,并不是通过这个User来进行通知,而是根据ActivityMainBindingImpl这个BaseObservable来通知,那么这个时候并不会通过ActivityMainBindingImpl的调用notifyPropertyChanged()最终拿到User中的PropertyChangeRegistry对象mCallbacks,所以起作用的并不是这句话。而最终ActivityMainBindingImpl在设置User起刷新作用,是因为super.requestRebind()的调用也触发了mRebindRunnable任务的执行,其实就是没有通过PropertyChange来触发requestRebind()
这里其实就是调用的BaseObservable的notifyPropertyChanged()方法,因为ActivityMainBindingImpl是ViewDataBinding的子类,而ViewDataBinding继承了BaseObservable类
这里的mNotifier.notifyCallback其实就会调用到下面的PropertyChangeRegistry中定义的NOTIFIER_CALLBACK 属性中的onNotifyCallback实现,而这里的callback其实就是WeakPropertyListener,因为WeakPropertyListener是OnPropertyChangedCallback的子类,这里其实会回调给mLocalFieldObservers数组中所有的WeakListener
从mListener中取出target,而这里的mListener其实就是,WeakListener,而每个被观察者,其实都是有一个对应的LocalFieldId,这个id就是BR文件中定义的,刚才的流程中,我们传入的是0,所以这里的 mLocalFieldId=0
这里的onFieldChange的方法的实现,就是在ActivityMainBindingImpl.java中
这里因为fieldId=0,所以会进入第一个if条件if (fieldId == BR._all),所以会返回true,所以就会返回到ViewDataBinding.java中的handleFieldChange方法中,继续执行requestRebind()
这里最终都会执行mRebindRunnable的run()方法。只不过SDK版本大于等于16的时候,会采用Choreographer编舞者来处理,而之前的版本则是采用Handler来执行。
在这里最终就会执行到executeBindings()方法,而该方法的实现,又是在ActivityMainBindingImpl.java中
如果自定义类继承了BaseObservable类,则会更新注册监听。即BaseObservable保存PropertyChangeRegistry对象,该对象中会保存WeakPropertyListener监听,而WeakPropertyListener监听会持有WeakListener,WeakListener也会持有WeakPropertyListener,并且持有一个BaseObservable的target,这个target就是自定义的BaseObservable子类实现对象,在设置target的时候就会将WeakPropertyListener监听给保存在这个target中的PropertyChangeRegistry对象中,当使用自定义的BaseObservable进行更新的时候,就可以通过监听回调的方式通知到ActivityMainBindingImpl这些ViewDataBinding中,然后向ActivityMainBindingImpl解析得到的View实体中设置对应的数据。
通过WeakListener监听器中的ObservableReference对象保存观察者与被观察者,当被观察者发生改变的时候,就会找到对应的WeakListener监听器,然后通知观察者做修改。
而ObservableReference方法的实现,有多个,比如:WeakPropertyListener。
这里让WeakListener.setTarget()其实就是通过WeakPropertyListener给被观察者添加callback,然后当被观察者数据发生改变的时候,被观察者通过遍历其内部的PropertyChangeRegistry中的OnPropertyChangedCallback回调(其实就是WeakPropertyListener),然后通过WeakPropertyListener监听通知给ViewDataBinding以及其实现类ActivityMainBindingImpl具体进行数据的处理和设置。
其实就是向ViewModel或者自定义的Observable(是databinding中的Observable)的子类实现中的mCallbacks中添加监听WeakPropertyListener,用于数据变化回调。而在WeakPropertyListener中的WeakListener对象会保存这个Observable为target,用于在更新的时候取出。
比如在xml布局的data中直接使用Boolean、Integer、String等类型
如果ActivityMainBindingImpl中设置的是比如ViewModel,那么就需要看是否使用了LiveData修饰的属性,如果没有使用LiveData的,则并不会去更新注册监听信息,而只是重新保存ViewDataBinding中保存的数据实体,并且直接调用ActivityMainBinding中保存的View实体进行设置新的数据
如果是使用了LiveData的话,则会在调用LiveDataListener(这是一个Observer子类与上面的WeakPropertyListener类似)的addListener的时候,就会给LiveData注册观察者LiveDataListener,然后在LiveDataListener中的onChanged实现中通过调用ViewDataBinding的handleFieldChange方法触发数据变化修改,进而更新View显示的数据
使用 android-databinding 方法格式化友好的日期
【中文标题】使用 android-databinding 方法格式化友好的日期【英文标题】:Format friendly Date using android-databinding approach 【发布时间】:2019-03-03 07:01:59 【问题描述】:我想将我的日期 YYYY/MM/DD 格式化为更友好的模式。
我使用 android-databinding。
我的预期输出应该是示例:2006 年 8 月 22 日,星期二。 我当前来自 Json 的输入是“2018-09-27”(模型中的字符串数据)
我的代码:
public class DateUtils
SimpleDateFormat fromServer = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat myFormat = new SimpleDateFormat("dddd, dd MMMM yyyy");
public String getDateToFromat (String reciveDate)
String newFormatString = myFormat.format(fromServer.parse(reciveDate));
return newFormatString;
;
我的布局:
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data class ="CurrencyBindingDetailItem">
<import type="com.example.htw.currencyconverter.utils.DateUtils"/>
<import type="android.view.View" />
<variable name="currencyItemDetailDate" type="com.example.htw.currencyconverter.model.CurrencyDate"/>
<variable name="currencyBindingItemDetail" type="com.example.htw.currencyconverter.model.CurrencyBinding"/>
<variable name="callback" type="com.example.htw.currencyconverter.callback.ClickCallback"/>
</data>
<TextView
android:textSize="28dp"
android:text="@DateUtils.getDateToFromat(currencyItemDetailDate.date)"
android:textColor="@color/primary_text"
android:id="@+id/date_text"
android:layout_
android:layout_
android:layout_gravity="center" />
我确实有错误:
Found data binding errors.
****/ data binding error ****msg:**cannot find method getDateToFromat**(java.lang.String) in class com.example.htw.currencyconverter.utils.DateUtils
我确实清理并重新启动并重建。
【问题讨论】:
【参考方案1】:您为什么不创建一个数据绑定适配器,让您的 xml 保持更清晰?由于您来自服务器的日期是字符串格式,因此适配器将如下所示:
@BindingAdapter("bindServerDate")
public static void bindServerDate(@NonNull TextView textView, String date)
/*Parse string data and set it in another format for your textView*/
它的用法:
在您的 viewModel 中创建 ObservableField<String> serverDate
并从您的响应中设置值,在 xml 中设置 app:bindServerDate="@viewModel.serverDate"
。不要忘记将viewModel
添加为variable
并从您的activity/fragment
设置它
【讨论】:
我会试试这个方法,谢谢!【参考方案2】:您需要两个 DateFormat
对象。一个用于格式化您从服务器收到的字符串,另一个用于您想要的格式。
SimpleDateFormat fromServer = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat myFormat = new SimpleDateFormat("dddd, dd MMMM yyyy");
String inputDateStr="2018-09-27";
Date date = fromServer.parse(inputDateStr);
String outputDateStr =myFormat.format(date);
【讨论】:
为了更好地查看代码,我不发表评论,而是在下面发送答案。【参考方案3】: @BindingAdapter("formatDate")
fun TextView.setDate(order_date: String)
var outputDate: String? = null
try
val curFormater = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss")
val postFormater = SimpleDateFormat("MMM dd, yyyy")
val dateObj = curFormater.parse(order_date)
outputDate = postFormater.format(dateObj)
this.setText(outputDate)
catch (e: ParseException)
e.printStackTrace()
【讨论】:
以上是关于Android-DataBinding原理分析的主要内容,如果未能解决你的问题,请参考以下文章
基于MVVM架构,结合阿里ARouter打造的一套Android-Databinding组件化开发方案