JetPack系列---Databinding源码分析

最近撸了一些Jetpack的源码,发现自己阅读源码的能力越来越强了,为自己的努力点赞,相信没有谁的努力会被辜负

Databinding本来是第一个就要撸的,但是当我进入源码世界之后,真的是被它弄糊涂了。找了半天没有什么头绪,最后只能把它放在最后来看了。即便是这样,把Databinding捋顺也用了半天的时间,期间还使用了反射来验证,也是麻烦至极。因为Databind所生成的代码文件,都是使用APT编译期生成的,所以我们可以使用debug打断点来找思路,一开始我自己没有注意这点,所以浪费了很多时间。行了,废话不多说,我们开始撸源码。

一、源码探索

撸码前,我先把使用的model模型贴出来,省的之后相关的内容大家会糊涂,关于MainActivity和activity_layout的内容我就不贴了,都是基本用法。

public class UserBean extends BaseObservable {
    private long id;
    private String name;
    private char sex;
    private int age;

    public UserBean(long id, String name, char sex, int age) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    @Bindable
    public long getId(){
        return id;
    }

    public void setId(long id){
        this.id = id;
        notifyPropertyChanged(BR.id);
    }

    @Bindable
    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public char getSex(){
        return sex;
    }

    public void setSex(char sex){
        this.sex = sex;
        notifyPropertyChanged(BR.sex);
    }

    @Bindable
    public int getAge(){
        return age;
    }

    public void setAge(int age){
        this.age = age;
        notifyPropertyChanged(age);
    }

Databinding的源码流程,我这里分了两步走:(1)DatabindingUtil的setContentView方法(2)ActivityMainBinding的setUser方法

1.DatabindingUtil的setContentView方法

MainActivity中是如下写:

ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);

进入DataBindingUtil的setContentView方法中

    public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
            int layoutId) {
        return setContentView(activity, layoutId, sDefaultComponent);
    }

    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);
    }

进入bindToAddedViews方法中

    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;
        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);
        }
    }

childredAdded等于1,所以我们进入第一个bind方法中

    private static DataBinderMapper sMapper = new DataBinderMapperImpl();

    static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
            int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    }

我们进入sMapper.getDataBinder方法中查看,发现是一个抽象方法,真正的实现类应该的文件在:app->build->generated->ap_generated_sources->debug->debug->out->包路径->DataBinderMapperImpl,我们在这个类里找到了相关的内容

  @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_ACTIVITYMAIN: {
          if ("layout/activity_main_0".equals(tag)) {
            return new ActivityMainBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }

我们进入ActivityMainBindingImpl的构造方法中

    public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));
    }
    private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 1
            , (androidx.recyclerview.widget.RecyclerView) bindings[5]
            );

        //此处省略部分代码

        setRootTag(root);
        // listeners
        invalidateAll();
    }

先执行了父类的构造方法,这个父类最后定位到了ViewDataBinding这个类,我们查看构造方法

(注:直接点击setRootTag方法就进入到了ViewDataBinding里了,点击继承的ActivitymainBinding类是进入不到父类的)

    static {
        if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
            ROOT_REATTACHED_LISTENER = null;
        } else {
            ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
                @TargetApi(VERSION_CODES.KITKAT)
                @Override
                public void onViewAttachedToWindow(View v) {
                    // execute the pending bindings.
                    final ViewDataBinding binding = getBinding(v);
                    binding.mRebindRunnable.run();
                    v.removeOnAttachStateChangeListener(this);
                }

                @Override
                public void onViewDetachedFromWindow(View v) {
                }
            };
        }
    }

    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mPendingRebind = false;
            }
            processReferenceQueue();

            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
                // Nested so that we don't get a lint warning in IntelliJ
                if (!mRoot.isAttachedToWindow()) {
                    // Don't execute the pending bindings until the View
                    // is attached again.
                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    return;
                }
            }
            executePendingBindings();
        }
    };    

    protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
        mBindingComponent = bindingComponent;
        mLocalFieldObservers = new WeakListener[localFieldCount];
        this.mRoot = root;
        if (Looper.myLooper() == null) {
            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
        }
        if (USE_CHOREOGRAPHER) {
            mChoreographer = Choreographer.getInstance();
            mFrameCallback = new Choreographer.FrameCallback() {
                @Override
                public void doFrame(long frameTimeNanos) {
                    mRebindRunnable.run();
                }
            };
        } else {
            mFrameCallback = null;
            mUIThreadHandler = new Handler(Looper.myLooper());
        }
    }

这里面有三个内容:

(1)静态代码块:静态代码块中主要是一个监听,简单来说就是,当View和屏幕绑定时,这个监听就会触发,这里指的是mRootView

(2)mRebindRunnable:当静态代码块中的监听执行时,这个类的run方法就执行了

(3)构造方法:这里使用了Choreographer这个类。关于这个类,推荐大家一篇文章

我们再回到ActivityMainBindingImpl的构造方法中,之后会执行invalidateAll方法,我们进入查看

    @Override
    public void invalidateAll() {
        synchronized(this) {
                mDirtyFlags = 0x20L;
        }
        requestRebind();
    }

这个requestRebind方法是父类ViewDataBinding的方法,我们进去查看

    protected void requestRebind() {
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            final LifecycleOwner owner = this.mLifecycleOwner;
            if (owner != null) {
                Lifecycle.State state = owner.getLifecycle().getCurrentState();
                if (!state.isAtLeast(Lifecycle.State.STARTED)) {
                    return; // wait until lifecycle owner is started
                }
            }
            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                mPendingRebind = true;
            }
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

从这里我们可以看到上面说的mChoreographer,它执行了postFrameCallback方法,当doFrame方法执行时,mRebindRunnablerun方法就执行了。mRebindRunnable中的run方法执行了两个内容:如果mRoot已经和屏幕进行了绑定,那么直接执行executePendingBindings方法;否则的话,添加监听,什么时候绑定了,什么时候执行。所以我们进入executePendingBindings方法中查看:

    public void executePendingBindings() {
        if (mContainingBinding == null) {
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }

进入executeBindingsInternal方法中查看:

    private void executeBindingsInternal() {
        if (mIsExecutingPendingBindings) {
            requestRebind();
            return;
        }
        if (!hasPendingBindings()) {
            return;
        }
        mIsExecutingPendingBindings = true;
        mRebindHalted = false;
        if (mRebindCallbacks != null) {
            mRebindCallbacks.notifyCallbacks(this, REBIND, null);

            // The onRebindListeners will change mPendingHalted
            if (mRebindHalted) {
                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
            }
        }
        if (!mRebindHalted) {
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
    }

这里会进入到executeBindings方法中(如果某些变量拿不准,可以debug打断点看看),这个方法是一个抽象方法,具体的实现在ActivityMainBindingImpl中,我们进入查看

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        int userAge = 0;
        com.gzc.databinding.UserBean user = mUser;
        java.lang.String stringValueOfUserAge = null;
        java.lang.String userName = null;
        java.lang.String javaLangStringStringValueOfUserAge = null;
        java.lang.String javaLangStringStringValueOfUserSex = null;
        java.lang.String javaLangStringIdUserId = null;
        java.lang.String javaLangStringUserName = null;
        char userSex = '\u0000';
        java.lang.String stringValueOfUserSex = null;
        long userId = 0;

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


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

                    if (user != null) {
                        // read user.age
                        userAge = user.getAge();
                    }


                    // read String.valueOf(user.age)
                    stringValueOfUserAge = java.lang.String.valueOf(userAge);


                    // read ("年龄:") + (String.valueOf(user.age))
                    javaLangStringStringValueOfUserAge = ("年龄:") + (stringValueOfUserAge);
            }
            if ((dirtyFlags & 0x25L) != 0) {

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


                    // read ("姓名:") + (user.name)
                    javaLangStringUserName = ("姓名:") + (userName);
            }
            if ((dirtyFlags & 0x29L) != 0) {

                    if (user != null) {
                        // read user.sex
                        userSex = user.getSex();
                    }


                    // read String.valueOf(user.sex)
                    stringValueOfUserSex = java.lang.String.valueOf(userSex);


                    // read ("性别:") + (String.valueOf(user.sex))
                    javaLangStringStringValueOfUserSex = ("性别:") + (stringValueOfUserSex);
            }
            if ((dirtyFlags & 0x23L) != 0) {

                    if (user != null) {
                        // read user.id
                        userId = user.getId();
                    }


                    // read ("id:") + (user.id)
                    javaLangStringIdUserId = ("id:") + (userId);
            }
        }
        // batch finished
        if ((dirtyFlags & 0x23L) != 0) {
            // api target 1

            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, javaLangStringIdUserId);
        }
        if ((dirtyFlags & 0x25L) != 0) {
            // api target 1

            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, javaLangStringUserName);
        }
        if ((dirtyFlags & 0x29L) != 0) {
            // api target 1

            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView3, javaLangStringStringValueOfUserSex);
        }
        if ((dirtyFlags & 0x31L) != 0) {
            // api target 1

            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView4, javaLangStringStringValueOfUserAge);
        }
    }

我们仔细查看会发现,在这里给了xml文件中和model进行绑定了的组件附了值,不过呢,此时的mUser还是空。

至此,setContentView的流程就已经结束了,我贴上一张流程图,便于理解

我们继续下一步探索

2.ActivityMainBinding的setUser方法

我们进入ActivityMainBindingImpl中的setUser中查看

    public void setUser(@Nullable com.gzc.databinding.UserBean User) {
        updateRegistration(0, User);
        this.mUser = User;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.user);
        super.requestRebind();
    }

到了这里,估计小伙伴会像我之前一样,一个一个方法的进入查找线索,然后你打断点会发现,会快就出来了,不论是notifyPropertyChanged还是super.requestRebind方法(注:我调用setUser这个方法是在Activity的onCreate方法中)。那设置完mUser后,信息是如何在布局文件中更新的呢?小伙伴应该还记得上面有和屏幕绑定的代码,如果绑定成功之后,自然会执行mRebindRunnablerun方法,之后的话和1一样了。

有的小伙伴估计会问,如果我不是在onCreate方法调用的setUser呢,我在屏幕绑定之后调用的会怎么样?那我们继续看setUser这个方法,notifyPropertyChanged这个方法依然忽略,因为里面的mCallback依然是空,直接return了。有的同学会说:等等,别欺负我没看过源码,源码我也看了,从updateRegistration进入,一层一层的会到ViewDataBinding中的registerTo中,如下

    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return;
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            listener = listenerCreator.create(this, localFieldId);
            mLocalFieldObservers[localFieldId] = listener;
            if (mLifecycleOwner != null) {
                listener.setLifecycleOwner(mLifecycleOwner);
            }
        }
        listener.setTarget(observable);
    }

然后到WekListenersetTarget

        public void setTarget(T object) {
            unregister();
            mTarget = object;
            if (mTarget != null) {
                mObservable.addListener(mTarget);
            }
        }

mObservale的具体实现类是WeakPropertyListener

        @Override
        public void addListener(Observable target) {
            target.addOnPropertyChangedCallback(this);
        }

addOnPropertyChanedCallbackBaseObservable中有实现

    @Override
    public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
        synchronized (this) {
            if (mCallbacks == null) {
                mCallbacks = new PropertyChangeRegistry();
            }
        }
        mCallbacks.add(callback);
    }

你看看,mCallbacks明明被创建了,你还说是空?

我之前也在这里糊涂了一阵,我们要明白:上面的target是谁?是通过setUser传过来的User对象!而不是ActivityMainBinding!ActivityMainBinding所持有的mCallback还是空的呢!

我们看下super.requestRebind方法

    protected void requestRebind() {
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            final LifecycleOwner owner = this.mLifecycleOwner;
            if (owner != null) {
                Lifecycle.State state = owner.getLifecycle().getCurrentState();
                if (!state.isAtLeast(Lifecycle.State.STARTED)) {
                    return; // wait until lifecycle owner is started
                }
            }
            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                mPendingRebind = true;
            }
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

其实和1中的一样,最后执行到了mRebindRunnable中的run方法,最后定位到1中最后说的executeBindings方法。

流程图我就不贴了,因为基本上和1中的图相同

二、问题

1.为什么说databinding或者说MVVM比较耗内存?

我认为从上面这么绕的代码上也能知道内存的消耗肯定要比非databinding要耗内存,另外,布局文件会通过APT方式额外的生成两个副本。所以内存的消耗,可想而知。

2.我们在User的get方法中用到了@Bindable注解,有什么用?

主要是生成BR中索引

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值