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&lt;String&gt; 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原理分析的主要内容,如果未能解决你的问题,请参考以下文章

Android-DataBinding使用

基于MVVM架构,结合阿里ARouter打造的一套Android-Databinding组件化开发方案

kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析

ContentProvider原理分析

SurfaceFlinger 原理分析

kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析