jetpack系列笔记

jetpack系列笔记

一、jetpack系列之Lifecycle
二、jetpack系列之LiveData
三、jetpack系列之ViewModel
四、jetpack系列之DataBinding



一、DataBinding是什么?

官方定义:

数据绑定库是一个支持库,可让您使用声明性格式(而不是以程序化方式)将布局中的界面组件绑定到应用中的数据源。

二、使用步骤

1.先打开开关:

build.gradle.kts中将dataBinding 设置为true

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

2.修改布局文件

修改为Databinding的布局文件。可以在原有布局文件中,右键->Show Context Actions->Convert to data binding layout.
代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="user"
            type="com.wyx.jetpack.dataBinding.User" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".dataBinding.DataBindingActivity">

        <TextView
            android:id="@+id/name_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

        <TextView
            android:id="@+id/pwd_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.pwd}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/name_text"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

variable属性中,name名字可以随意取,type是我们自定义的实体类。然后使用@{ }语法,将实体类中的变量赋值给控件,如:android:text=“@{user.name}”, 其中user是variable属性中的name。

3.创建一个实体类

import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import androidx.databinding.library.baseAdapters.BR

class User() : BaseObservable() {

    @Bindable
    var name: String = "hello world"
        set(value) {
            field = value
            notifyPropertyChanged(BR.name)
        }

    @Bindable
    var pwd: String = "123456"
        set(value) {
            field = value
            notifyPropertyChanged(BR.pwd)
        }
}
  1. 类需要继承BaseObservable()
  2. 变量需要get()方法添加@Bindble注解
  3. 添加notifyPropertyChanged(BR.属性名),BR为系统自动生成,可以先理解为固定用法。
    注:本文使用User作为实体,APT产生的代码会有setUser()类似方法,实际使用不同的实体,会产生的名字也不同。

4.在Activity中调用

class DataBindingActivity : AppCompatActivity() {
    private lateinit var binding:ActivityDataBindingBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this@DataBindingActivity, R.layout.activity_data_binding)

        val user = User()
        user.name = "Yoshawn"
        user.pwd = "123456"

        //设置整个变量
        binding.user = user
        //设置某个属性
        user.name = "Jack"
        //直接使用控件
        binding.nameText.text = "David" 
    }
}
  1. 使用DataBindingUtil.setContentView(this@DataBindingActivity, R.layout.activity_data_binding) 代替原来的setContentView方法。
  2. 使用binding.user = user 给binding的进行赋值

三、Databinding原理

1、View绑定过程

Databinding使用了注解处理器,利用编译时技术,帮我们生成了很多代码去代替我们的手动操作。生成的文件路径为build\generated\source\kapt\debug\包路径, 可以看到,这里有我们要使用的BR文件,还有其他我们需要用到的文件。
  1. 重新生成了一个布局文件,布局文件路径为:build/intermediates/data_binding_layout_info_type_merge/debug/mergeDebugResources/out/activity_data_binding-layout.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="jetpack\src\main\res\layout\activity_data_binding.xml"
    isBindingData="true" isMerge="false"
    layout="activity_data_binding" modulePackage="com.wyx.jetpack"
    rootNodeType="androidx.constraintlayout.widget.ConstraintLayout" rootNodeViewId="@+id/main">
    <Variables name="user" declared="true" type="com.wyx.jetpack.dataBinding.User">
        <location endLine="8" endOffset="53" startLine="6" startOffset="8" />
    </Variables>
    <Targets>
        <Target id="@+id/main" tag="layout/activity_data_binding_0"
            view="androidx.constraintlayout.widget.ConstraintLayout">
            <Expressions />
            <location endLine="33" endOffset="55" startLine="11" startOffset="4" />
        </Target>
        <Target id="@+id/name_text" tag="binding_1" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="user.name">
                    <Location endLine="21" endOffset="38" startLine="21" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="21" endOffset="36" startLine="21" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="23" endOffset="54" startLine="17" startOffset="8" />
        </Target>
        <Target id="@+id/pwd_text" tag="binding_2" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="user.pwd">
                    <Location endLine="29" endOffset="37" startLine="29" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="29" endOffset="35" startLine="29" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="31" endOffset="65" startLine="25" startOffset="8" />
        </Target>
    </Targets>
</Layout>

可以看到,这个文件将我们的布局,每一个控件的属性记录了下来,此外,还增加了一个tag属性,tag属性的值都是binding_1,binding_2依次往下排。也可以看一下文件build/intermediates/incremental/debug/mergeDebugResources/stripped.dir/layout/activity_data_binding.xml,可以看到跟我们写的布局文件很相似,但是多了一个tag属性。我们可以根据tag属性,能找到该控件的所有信息。

  1. view的绑定过程:
   binding = DataBindingUtil.setContentView(this@DataBindingActivity, R.layout.activity_data_binding)

   //调用setContentView
   public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
           int layoutId) {
       return setContentView(activity, layoutId, sDefaultComponent);
   }
   
   //调用setContentView
   public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
           int layoutId, @Nullable DataBindingComponent bindingComponent) {
       activity.setContentView(layoutId);
       View decorView = activity.getWindow().getDecorView();
       ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
       return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
   }

此处还是调用了activity的setContentView方法,代替我们手动调用。这里,还从DecorView中获取到了顶层的ContentView。

	//调用bindToAddedViews(bindingComponent, contentView, 0, layoutId)
    private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
            ViewGroup parent, int startChildren, int layoutId) {
        final int endChildren = parent.getChildCount();
        final int childrenAdded = endChildren - startChildren;
        //如果只有一个View,直接进行绑定,如果有多个View,则对View进行遍历,然后去绑定。
        if (childrenAdded == 1) {
            final View childView = parent.getChildAt(endChildren - 1);
            return bind(component, childView, layoutId);
        } else {
            final View[] children = new View[childrenAdded];
            for (int i = 0; i < childrenAdded; i++) {
                children[i] = parent.getChildAt(i + startChildren);
            }
            return bind(component, children, layoutId);
        }
    }
	
	//调用bind(component, childView, layoutId);
    static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
            int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    }
	
	//sMapper的定义
 	private static DataBinderMapper sMapper = new DataBinderMapperImpl();

这里DataBinderMapperImpl是APT自动生成的一个类,在这个类里继续做绑定操作。

  @Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_ACTIVITYDATABINDING: {
          if ("layout/activity_data_binding_0".equals(tag)) {
            return new ActivityDataBindingBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for activity_data_binding is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }

这里获取到View的tag标签。我们布局的最外层Layout的tag是layout/activity_data_binding_0,其实就是创建了一个类ActivityDataBindingBindingImpl。这个类也是编译器自动给我们生成的,看一下这个类的创建过程。

	public ActivityDataBindingBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
	    this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
	}
    private ActivityDataBindingBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 1
            , (androidx.constraintlayout.widget.ConstraintLayout) bindings[0]
            , (android.widget.TextView) bindings[1]
            , (android.widget.TextView) bindings[2]
            );
        this.main.setTag(null);
        this.nameText.setTag(null);
        this.pwdText.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

构造方法进行了一下重载。调用了三个参数的构造方法。看下第三个参数Object[] 是什么。

	//apBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds)
    protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
            int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
        Object[] bindings = new Object[numBindings];
        mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
        return bindings;
    }

    //mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
   private static void mapBindings(DataBindingComponent bindingComponent, View view,
            Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
            boolean isRoot) {
		...
        Object objTag = view.getTag();
        final String tag = (objTag instanceof String) ? (String) objTag : null;
        boolean isBound = false;
		//如果以tag是layout开头,说明是Layout,将View放进到bindings数组中
        if (isRoot && tag != null && tag.startsWith("layout")) {
            final int underscoreIndex = tag.lastIndexOf('_');
            if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
                final int index = parseTagInt(tag, underscoreIndex + 1);
                if (bindings[index] == null) {
                    bindings[index] = view;
                }
                indexInIncludes = includes == null ? -1 : index;
                isBound = true;
            } else {
                indexInIncludes = -1;
            }
        //如果以bingding_ 开头,放进View中
        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
            int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
            if (bindings[tagIndex] == null) {
                bindings[tagIndex] = view;
            }
            isBound = true;
            indexInIncludes = includes == null ? -1 : tagIndex;
        } else {
            // Not a bound view
            indexInIncludes = -1;
        }
		...
		//如果是ViewGroup,还要递归遍历其子View,放到bindings数组中
        if (view instanceof  ViewGroup) {
            final ViewGroup viewGroup = (ViewGroup) view;
            final int count = viewGroup.getChildCount();
            int minInclude = 0;
            for (int i = 0; i < count; i++) {
                final View child = viewGroup.getChildAt(i);
                boolean isInclude = false;
                if (indexInIncludes >= 0 && child.getTag() instanceof String) {
                    String childTag = (String) child.getTag();
                    if (childTag.endsWith("_0") &&
                            childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
                        // This *could* be an include. Test against the expected includes.
                        int includeIndex = findIncludeIndex(childTag, minInclude,
                                includes, indexInIncludes);
                        if (includeIndex >= 0) {
                            isInclude = true;
                            minInclude = includeIndex + 1;
                            final int index = includes.indexes[indexInIncludes][includeIndex];
                            final int layoutId = includes.layoutIds[indexInIncludes][includeIndex];
                            int lastMatchingIndex = findLastMatching(viewGroup, i);
                            if (lastMatchingIndex == i) {
                            	//在这里去递归调用
                                bindings[index] = DataBindingUtil.bind(bindingComponent, child,
                                        layoutId);
                            } else {
                                ...
                            }
                        }
                    }
                }
                if (!isInclude) {
                    mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
                }
            }
        }
    }

把所有的View对象,都保存在了bindings这个Object数组中。然后调用ActivityDataBindingBindingImpl的第二个构造方法,将View进行绑定。这个时候我们就可以在Activity中使用databinding中的view了。

2、数据驱动的过程

  1. databind过程中,先介绍其中用到的几个类的作用:
    在这里插入图片描述

  2. 数据驱动的过程:
    首先用户调用setUser:

    //ActivityViewDataBindingImpl.java
    public void setUser(@Nullable com.wyx.jetpack.dataBinding.User User) {
    	//更新注册的User
        updateRegistration(0, User);
        this.mUser = User;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        //通知View数据变化
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }

注册User的过程:

	//ViewDataBinding.java
    protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
    }

CREATE_PROPERTY_LISTENER 就是一个CreateWeakListener,通过WeakPropertyListener的getListener()创建一个WeakListener:

    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(
                ViewDataBinding viewDataBinding,
                int localFieldId,
                ReferenceQueue<ViewDataBinding> referenceQueue
        ) {
            return new WeakPropertyListener(viewDataBinding, localFieldId, referenceQueue)
                    .getListener();
        }
    };

继续看注册过程:

	//ViewDataBinding.java
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    protected boolean updateRegistration(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return unregisterFrom(localFieldId);
        }
        //1.先从mLocalFieldObservers看有没有缓存的WeakListener 
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
        	//2、缓存中没有WeakListener ,进行创建并注册
            registerTo(localFieldId, observable, listenerCreator);
            return true;
        }
        if (listener.getTarget() == observable) {
            return false;//nothing to do, same object
        }
        //3、缓存中存在WeakListener ,重新注册
        unregisterFrom(localFieldId);
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }
    
    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return;
        }
        //4、再次在缓存中检查知否存在
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
        	//5、调用CREATE_PROPERTY_LISTENER也创建WeakListener,其实最终调用了WeakPropertyListener 的getListener()方法。
            listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
           	//6、将WeakListener放入缓存中
            mLocalFieldObservers[localFieldId] = listener;
            if (mLifecycleOwner != null) {
                listener.setLifecycleOwner(mLifecycleOwner);
            }
        }
        //7、这里是将WeakPropertyListener 作为观察者,注册保存在BaseObservable中,也就是我们自定义实体的父类。
        listener.setTarget(observable);
    }

可以看到,WeakPropertyListener 创建并持有WeakListener对象 ,WeakListener 同时也持有WeakPropertyListener 对象。可以通过WeakListener 控制WeakPropertyListener。下面看一下第七步如何将WeakPropertyListener注册进BaseObservable中的:

	//WeakListener.java
    public void setTarget(T object) {
        unregister();
        mTarget = object;
        if (mTarget != null) {
            mObservable.addListener(mTarget);
        }
    }
	//ViewDataBinding.WeakPropertyListener
    @Override
    public void addListener(Observable target) {
    	//this 为WeakPropertyListener
        target.addOnPropertyChangedCallback(this);
    }
	//BaseObservable.java
	@Override
    public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
        synchronized (this) {
            if (mCallbacks == null) {
                mCallbacks = new PropertyChangeRegistry();
            }
        }
        mCallbacks.add(callback);
    }

	//CallbackRegistry.java
    public synchronized void add(C callback) {
        if (callback == null) {
            throw new IllegalArgumentException("callback cannot be null");
        }
        int index = mCallbacks.lastIndexOf(callback);
        if (index < 0 || isRemoved(index)) {
            mCallbacks.add(callback);
        }
    }

BaseObservable持有PropertyChangeRegistry, PropertyChangeRegistry的父类是CallbackRegistry。在CallbackRegistry中保存了一个List,用来存放所有的WeakPropertyListener。到这里,将WeakPropertyListener注册进了BaseObservable中。

回过头来看调用setUser的过程,观察者和被观察者已经绑定,接下来往下看:

	//BaseObservable.java
    public void setUser(@Nullable com.wyx.jetpack.dataBinding.User User) {
        updateRegistration(0, User);
        this.mUser = User;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }

mDirtyFlags 记录了哪个属性或字段进行了改变,是BR中定义的值。mDirtyFlags |= 0x1L 代表是整个User。
notifyPropertyChanged(BR.user) 是通知整个User进行了变更。在我们的User实体中,单个变量变化,调用的也是这个方法。下面是这个调用时序,可以自己自己去读一下细节:

  1. BaseObservable.java : notifyPropertyChanged(int fieldId)
  2. BaseObservable.java : mCallbacks.notifyCallbacks(this, fieldId, null);
  3. CallbackRegistry.java : notifyRecurse(sender, arg, arg2);
  4. CallbackRegistry.java : notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
  5. CallbackRegistry.java : mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);

mNotifier是在PropertyChangeRegistry.java中定义,并传给父类的:

    private static final CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {
        @Override
        public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
                int arg, Void notUsed) {
            callback.onPropertyChanged(sender, arg);
        }
    };

    public PropertyChangeRegistry() {
        super(NOTIFIER_CALLBACK);
    }

继续刚才的调用:

  1. PropertyChangeRegistry.java : callback.onPropertyChanged(sender, arg);
  2. ViewDatabing.java : handleFieldChange(int mLocalFieldId, Object object, int fieldId)

看下ViewDatabing.java 中handleFieldChange的实现:

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    protected void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        if (mInLiveDataRegisterObserver || mInStateFlowRegisterObserver) {
            return;
        }
        //1、是否能找到需要变更的字段
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
        	//2、请求执行
            requestRebind();
        }
    }

先看一下如何判断需要变更的字段的:

    @Override
    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        switch (localFieldId) {
            case 0 :
                return onChangeUser((com.wyx.jetpack.dataBinding.User) object, fieldId);
        }
        return false;
    }
    private boolean onChangeUser(com.wyx.jetpack.dataBinding.User User, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x1L;
            }
            return true;
        }
        else if (fieldId == BR.name) {
            synchronized(this) {
                    mDirtyFlags |= 0x2L;
            }
            return true;
        }
        else if (fieldId == BR.pwd) {
            synchronized(this) {
                    mDirtyFlags |= 0x4L;
            }
            return true;
        }
 
        return false;
    }

将mDirtyFlags 与字段在BR文件中对应的数值进行或操作,mDirtyFlags 初始值为0x10L,在invalidateAll() 中进行赋值的。为什么是0x8L?这是因为实体里有1个字段,值为2的3次方。实体里有n个字段,mDirtyFlags 的值为2的 n+1次方。这样,需要修改的字段,都记录在mDirtyFlags 中。

再看下requestRebind()是如何执行的:

    protected void requestRebind() {
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            ...
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

USE_CHOREOGRAPHER判断是在SDK大于16后,都交到编舞者Choreographer中执行的。编舞者Choreographer是UI绘制一个很重要的类,交给编舞这去执行,可以提高性能,减少卡顿发生。

        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                mRebindRunnable.run();
            }
        };

最终也是执行了mRebindRunnable 的run()方法:

	//ViewDataBinding
    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            ...
            executePendingBindings();
        }
    };


    public void executePendingBindings() {
        if (mContainingBinding == null) {
        	//执行这里
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }
    
    private void executeBindingsInternal() {
       ...
        if (!mRebindHalted) {
        	//执行这里
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
    }

执行了executeBindings(),executeBindings()实现是在ActivityDataBindingBindingImpl.java中。
接下来是赋值过程:

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        java.lang.String userName = null;
        com.wyx.jetpack.dataBinding.User user = mUser;
        boolean userSex = false;
        java.lang.String userPwd = null;
        java.lang.String stringValueOfUserSex = null;

        if ((dirtyFlags & 0x1fL) != 0) {


            if ((dirtyFlags & 0x13L) != 0) {

                    if (user != null) {
                        // read user.name
                        userName = user.getName();
                    }
            }
            if ((dirtyFlags & 0x15L) != 0) {

                    if (user != null) {
                        // read user.pwd
                        userPwd = user.getPwd();
                    }
            }
        }
        // batch finished
        if ((dirtyFlags & 0x13L) != 0) {
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.nameText, userName);
        }
        if ((dirtyFlags & 0x15L) != 0) {
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.pwdText, userPwd);
        }
    }

根据dirtyFlags的值,执行与操作,找到对应的字段进行赋值。如果需要修改userName值,此时dirtyFlags二进制为11,0x13L转换为二进制是10011,11 & 10011不为零,需要修改userName值。最终还是调用了View的setText方法去修改。


总结

DataViewBinding 使用了观察者模式,利用框架将观察者和被观察者进行绑定。观察者和被观察者是多对多的关系,当某个被观察者发生数据变化时,不再需要通过Map和两层for循环的方式进行回调,提高了效率。但是DataViewBinding会很占内存,而且不容易查找bug。

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值