Android Study 之 如何通过Data Binding提升撸码逼格(高级篇 - 简单阅读源码)

LZ-Says:洗个澡,突然感觉爽到爆~~~ 又回来了哦~

Android Study 之 如何通过Data Binding提升撸码逼格(高级篇 - 简单阅读源码)_第1张图片

前言

前几天,终于完善了关于Data Binding基础篇以及进阶篇博文编写,过程很是艰难哦~

下面附上链接地址:

  • Android Study 之 如何通过Data Binding提升撸码逼格(基础篇);

  • Android Study 之 如何通过Data Binding提升撸码逼格(进阶篇);

而今天,整整行囊,准备开启Data Binding高级篇,完成之后,也该开启Android重新回味之路了,长线计划,一定要缩短时间咯~~~

拖了好久咯~~~ MMP呦~

启程

Android Study 之 如何通过Data Binding提升撸码逼格(高级篇 - 简单阅读源码)_第2张图片

以下关于Data Binding分析基于Libary 1.3.1。

一、setContentView源码分析

首先我们来回顾下Data Binding最初的使用:

DataBindingUtil.setContentView

我们先深入进去,看看在这里面,它到底干了什么?

    /**
     * 设置Activity内容View为给定布局并返回关联绑定集。
     * 这里需要注意的是:给定的布局资源类型必须为非Merge Layout!
     *
     * @param activity 当前Activity
     * @param layoutId 当资源被引入时,绑定并且设置Activity内容
     * @return 绑定并关联引入Content View
     */
    public static  T setContentView(Activity activity, int layoutId) {
        return setContentView(activity, layoutId, sDefaultComponent);
    }

可以看到这里直接return了一个setContentView,并且将我们传递的参数继续发送。

一起来看看这里干了什么鬼?

    public static  T setContentView(Activity activity, int layoutId,
            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设置Content View,也就是将要展示的UI;

接着,获取了一个DecorView,那么为什么要获取它呢?这里LZ放置一张关于Activity布局层级关系图:

Android Study 之 如何通过Data Binding提升撸码逼格(高级篇 - 简单阅读源码)_第3张图片

可以很清晰的看到,在我们的Activity的层级关系,因此,拿到Content View上级,也就可以理解为拿到了对Content View的控制权。

接下来,直接拿到ContentView的实例,通过:decorView.findViewById(android.R.id.content)。

而最后,则调用bindToAddedViews方法进行处理,之后将结果Return。那么,鉴名其意,我们就可以知道这个方法主要的作用就是:绑定添加我们的View。

而在bindToAddedViews方法中,我们猜测下它会怎么做?来来来,老铁想想~

LZ这里猜测,既然是直接调用bindToAddedViews将结果Return,那么最终这里面应该是遍历当前Layout下所有的View,拿到之后进行绑定。

下面一起来看看到底进行了什么操作吧!

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

下面继续深入bind方法。

/**
 * Returns the binding for the given layout root or creates a binding if one
 * does not exist.
 * 

* Prefer using the generated Binding's bind method to ensure type-safe inflation * when it is known that root has not yet been bound. * * @param root The root View of the inflated binding layout. * @param bindingComponent The DataBindingComponent to use in data binding. * @return A ViewDataBinding for the given root View. If one already exists, the * existing one will be returned. * @throws IllegalArgumentException when root is not from an inflated binding layout. * @see #getBinding(View) */ @SuppressWarnings("unchecked") // 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型 public static T bind(View root, DataBindingComponent bindingComponent) { T binding = getBinding(root); if (binding != null) { return binding; } Object tagObj = root.getTag(); if (!(tagObj instanceof String)) { throw new IllegalArgumentException("View is not a binding layout"); } else { String tag = (String) tagObj; int layoutId = sMapper.getLayoutId(tag); if (layoutId == 0) { throw new IllegalArgumentException("View is not a binding layout"); } return (T) sMapper.getDataBinder(bindingComponent, root, layoutId); } } @SuppressWarnings("unchecked") static T bind(DataBindingComponent bindingComponent, View[] roots, int layoutId) { return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId); } static T bind(DataBindingComponent bindingComponent, View root, int layoutId) { return (T) sMapper.getDataBinder(bindingComponent, root, layoutId); }

这里可以看出,bind方法有支持俩种绑定方式,一种是单个View也就是参数中的root,一种是多个View也就是参数中的roots。

bind方法Return类型为T extends ViewDataBinding,也就是说这里处理后的结果就是我们在实例中实际使用的ActivityXXXBinding。

而sMapper又是什么?

private static DataBinderMapper sMapper = new DataBinderMapper();

下面我们简单看一下DataBinderMapper里面内置了什么内容?从命名上可以得出,这里存放一些类似Mapper的东西,会不会是相关的配置文件?或者说是生成的配置文件呢?一起来看一下~

package android.databinding;
import com.hlq.databindingdemo.BR;

class DataBinderMapper  {

    // 工程设置最低兼容SDK版本
    final static int TARGET_MIN_SDK = 22;

    // 无参构造
    public DataBinderMapper() {
    }

    // return绑定后的Data Binding
    public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
        switch(layoutId) {
                case com.hlq.databindingdemo.R.layout.item_love_history_show:
                    return com.hlq.databindingdemo.databinding.ItemLoveHistoryShowBinding.bind(view, bindingComponent);
                case com.hlq.databindingdemo.R.layout.activity_observable:
                    return com.hlq.databindingdemo.databinding.ActivityObservableBinding.bind(view, bindingComponent);
                ...
        }
        return null;
    }

    android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId) {
        switch(layoutId) {
        }
        return null;
    }

    // 获取layout Id
    int getLayoutId(String tag) {
        // 有效性校验
        if (tag == null) {
            return 0;
        }
        // 获取Layout ID Tag hashCode值
        final int code = tag.hashCode();
        switch(code) {
            case 813835775: {
                if(tag.equals("layout/item_love_history_show_0")) {
                    return com.hlq.databindingdemo.R.layout.item_love_history_show;
                }
                break;
            }
            case -1191836097: {
                if(tag.equals("layout/activity_observable_0")) {
                    return com.hlq.databindingdemo.R.layout.activity_observable;
                }
                break;
            }
            ... 
        }
        return 0;
    }

    // 将ID干成String
    String convertBrIdToString(int id) {
        if (id < 0 || id >= InnerBrLookup.sKeys.length) {
            return null;
        }
        return InnerBrLookup.sKeys[id];
    }

    // 这里便是在Xml中应用的命名空间时,我们指定的别名
    private static class InnerBrLookup {
        static String[] sKeys = new String[]{
            "_all"
            ,"bean"
            ...};
    }
}

接着继续查看bind方法中具体执行了什么操作:

/**
 * 返回一个绑定后的根布局或者创建一个不存在的绑定结果
 * 当已知root尚未绑定时,优先使用生成的Binding的bind方法来确保类型安全的范围
 * @param root 根布局是引入的绑定的Layout
 * @param bindingComponent 用于数据绑定的DataBindingComponent
 * @return 指定根视图的ViewDataBinding。如果已经存在,现有的将被返回
 * @throws 当引入Layout不输入绑定类型则抛出IllegalArgumentException
 * @see #getBinding(View)
 */
@SuppressWarnings("unchecked")
public static  T bind(View root,
        DataBindingComponent bindingComponent) {
    // 获取绑定
    T binding = getBinding(root);
    // 如果不等于空,直接返回现有
    if (binding != null) {
        return binding;
    }
    // 获取引入Layout tag
    Object tagObj = root.getTag();
    if (!(tagObj instanceof String)) {
        throw new IllegalArgumentException("View is not a binding layout");
    } else {
        String tag = (String) tagObj;
        int layoutId = sMapper.getLayoutId(tag);
        if (layoutId == 0) {
            throw new IllegalArgumentException("View is not a binding layout");
        }
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    }
}

看一下getBinding这里又是什么鬼?

/**
 * 检索负责给定视图布局根的绑定。如果没有绑定,则返回null,否则将DataBindingComponent设置成默认:{@link #setDefaultComponent(DataBindingComponent)}
 *
 * @param view 具有绑定的布局中的根View
 * @return 如果不是bind视图或者没有进行关联绑定直接返回null
 */
public static  T getBinding(View view) {
    return (T) ViewDataBinding.getBinding(view);
}

static ViewDataBinding getBinding(View v) {
    if (v != null) {
        // 校验当前最低兼容版本是否大于等于api 14 DataBinderMapper.TARGET_MIN_SDK >= 14
        if (USE_TAG_ID) { // 直接return获取到的tag
            return (ViewDataBinding) v.getTag(R.id.dataBinding);
        } else { // return是ViewDataBinding类型的tag
            final Object tag = v.getTag();
            if (tag instanceof ViewDataBinding) {
                return (ViewDataBinding) tag;
            }
        }
    }
    return null;
}

/**
 * 返回与此视图关联的标记和指定的键。
 *
 * @param key tag对应key
 *
 * @return Object存储在此视图中作为标记,如果未设置,则返回null
 *
 * @see #setTag(int, Object)
 * @see #getTag()
 */
public Object getTag(int key) {
    if (mKeyedTags != null) return mKeyedTags.get(key);
    return null;
}

简单了解下关于getLayoutId方法:

int getLayoutId(String tag) {
if (tag == null) {
    return 0;
}
final int code = tag.hashCode();
switch(code) {
    case 813835775: {
        if(tag.equals("layout/item_love_history_show_0")) {
            return com.hlq.databindingdemo.R.layout.item_love_history_show;
        }
        break;
    }
    case -1191836097: {
        if(tag.equals("layout/activity_observable_0")) {
            return com.hlq.databindingdemo.R.layout.activity_observable;
        }
        break;
    }
    ...
}
return 0;
}

根据Tag的hashCode返回对应layout。

到此,setContentView分析结束一个段落。

针对之前关于Data Binding的使用,结合我们刚刚分析的结果,我们简单回顾下:

  • 首先,改造布局,也就是添加layout,此处需要注意,不能是merge布局;

  • 接着拿到当前Activity、给定的Layout以及默认的DataBindingComponent。获取到Content View父级,也就是DecorView,目前取到控制权;

  • 而最后,则是通过遍历ChildView添加并绑定即可。当然这里面忽略了很多细节,例如我们的tag取值,具体绑定操作。有兴趣可自行了解下,LZ这里就不过多的阐述了。

二、inflate分析

上述讲述了setContentView,这里一起来看下关于infate源码,看看他们之间又何异同性?

/**
 * 引用一个绑定布局并返回该布局的新创建的绑定。
 * 这使用了设置的DataBindingComponent
 * {@link #setDefaultComponent(DataBindingComponent)}.
 * 
 * 除非layoutId是未知的,才使用此版本。否则,使用生成的绑定的引用方法来确保类型安全的引用
 *
 * @param inflater The LayoutInflater used to inflate the binding layout.
 * @param layoutId The layout resource ID of the layout to inflate.
 * @param parent Optional view to be the parent of the generated hierarchy
 *               (if attachToParent is true), or else simply an object that provides
 *               a set of LayoutParams values for root of the returned hierarchy
 *               (if attachToParent is false.)
 * @param attachToParent Whether the inflated hierarchy should be attached to the
 *                       parent parameter. If false, parent is only used to create
 *                       the correct subclass of LayoutParams for the root view in the XML.
 * @return The newly-created binding for the inflated layout or null if
 * the layoutId wasn't for a binding layout.
 * @throws InflateException When a merge layout was used and attachToParent was false.
 * @see #setDefaultComponent(DataBindingComponent)
 */
public static  T inflate(LayoutInflater inflater, int layoutId,
        @Nullable ViewGroup parent, boolean attachToParent) {
    return inflate(inflater, layoutId, parent, attachToParent, sDefaultComponent);
}

/**
 * Inflates a binding layout and returns the newly-created binding for that layout.
 * 

* Use this version only if layoutId is unknown in advance. Otherwise, use * the generated Binding's inflate method to ensure type-safe inflation. * * @param inflater The LayoutInflater used to inflate the binding layout. * @param layoutId The layout resource ID of the layout to inflate. * @param parent Optional view to be the parent of the generated hierarchy * (if attachToParent is true), or else simply an object that provides * a set of LayoutParams values for root of the returned hierarchy * (if attachToParent is false.) * @param attachToParent Whether the inflated hierarchy should be attached to the * parent parameter. If false, parent is only used to create * the correct subclass of LayoutParams for the root view in the XML. * @param bindingComponent The DataBindingComponent to use in the binding. * @return The newly-created binding for the inflated layout or null if * the layoutId wasn't for a binding layout. * @throws InflateException When a merge layout was used and attachToParent was false. */ public static T inflate( LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent, DataBindingComponent bindingComponent) { final boolean useChildren = parent != null && attachToParent; final int startChildren = useChildren ? parent.getChildCount() : 0; final View view = inflater.inflate(layoutId, parent, attachToParent); if (useChildren) { return bindToAddedViews(bindingComponent, parent, startChildren, layoutId); } else { return bind(bindingComponent, view, layoutId); } }

而这里的逻辑,则相对简单一些判断是否ContentView有内容,有则需要逐个获取并添加绑定,无则直接绑定即可。

而下面的操作流程则与setContentView一样了。

一个相当于直接引用布局,直接拿到了布局的控制权,而另一个则是需要去获取上级,通过获取上级拿到控制权进行操作。

三、生成Util类源码阅读

怎么看源码呢?

在Android Studio 3.1.1中,可以按照下图所示查看:

Android Study 之 如何通过Data Binding提升撸码逼格(高级篇 - 简单阅读源码)_第4张图片

首先看一波LZ实例中的MainActivity,经过Data Binding转化后如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.hlq.databindingdemo.databinding;

import android.databinding.DataBindingComponent;
import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.databinding.ViewDataBinding.IncludedLayouts;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ScrollView;
import com.hlq.databindingdemo.MainActivity.Presenter;

// 继承ViewDataBinding
public class ActivityMainBinding extends ViewDataBinding {

    @Nullable
    private static final IncludedLayouts sIncludes = null;

    @Nullable
    private static final SparseIntArray sViewsWithIds = null;

    @NonNull
    public final Button bindData;

    @NonNull
    public final Button bingListener;

    @NonNull
    public final Button imageView;
    ...

    @NonNull
    private final ScrollView mboundView0;

    @Nullable
    private Presenter mPersenter;

    private ActivityMainBinding.OnClickListenerImpl mPersenterOnClickAndroidViewViewOnClickListener;

    private long mDirtyFlags = -1L;

    public ActivityMainBinding(@NonNull DataBindingComponent bindingComponent, @NonNull View root) {
        super(bindingComponent, root, 0);
        // 从map中读取配置信息 这里面包含View中的控件等
        Object[] bindings = mapBindings(bindingComponent, root, 13, sIncludes, sViewsWithIds);
        // 实例化以及设置tag
        this.bindData = (Button)bindings[1];
        this.bindData.setTag((Object)null);
        ...
        this.setRootTag(root);
        // 进行view的异步刷新
        this.invalidateAll();
    }

    // 执行View异步刷新
    public void invalidateAll() {
        synchronized(this) {
            this.mDirtyFlags = 2L;
        }

        this.requestRebind();
    }

    // 判断是否有Observable化的字段数据被更新
    public boolean hasPendingBindings() {
        synchronized(this) {
            return this.mDirtyFlags != 0L;
        }
    }

    // 设置xml中引用的viewModel
    public boolean setVariable(int variableId, @Nullable Object variable) {
        boolean variableSet = true;
        if (12 == variableId) {
            this.setPersenter((Presenter)variable);
        } else {
            variableSet = false;
        }

        return variableSet;
    }

    // 设置事件
    public void setPersenter(@Nullable Presenter Persenter) {
        this.mPersenter = Persenter;
        // 设置同步锁
        synchronized(this) {
            this.mDirtyFlags |= 1L;
        }

        this.notifyPropertyChanged(12);
        super.requestRebind();
    }

    @Nullable
    public Presenter getPersenter() {
        return this.mPersenter;
    }

    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        return false;
    }

    // 执行绑定
    protected void executeBindings() {
        long dirtyFlags = 0L;
        synchronized(this) {
            dirtyFlags = this.mDirtyFlags;
            this.mDirtyFlags = 0L;
        }

        Presenter persenter = this.mPersenter;
        OnClickListener persenterOnClickAndroidViewViewOnClickListener = null;
        if ((dirtyFlags & 3L) != 0L && persenter != null) {
            persenterOnClickAndroidViewViewOnClickListener = (this.mPersenterOnClickAndroidViewViewOnClickListener == null ? (this.mPersenterOnClickAndroidViewViewOnClickListener = new ActivityMainBinding.OnClickListenerImpl()) : this.mPersenterOnClickAndroidViewViewOnClickListener).setValue(persenter);
        }

        if ((dirtyFlags & 3L) != 0L) {
            this.bindData.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.bingListener.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.imageView.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.normalRecyclerView.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.observableFieldStudy.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.showLoveHistory.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.showLoveHistoryOnClick.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.theWordForMe.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.updateData.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.useExpression.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.useInclude.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
            this.useViewStub.setOnClickListener(persenterOnClickAndroidViewViewOnClickListener);
        }

    }

    @NonNull
    public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup root, boolean attachToRoot) {
        return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
    }

    @NonNull
    public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup root, boolean attachToRoot, @Nullable DataBindingComponent bindingComponent) {
        return (ActivityMainBinding)DataBindingUtil.inflate(inflater, 2131296288, root, attachToRoot, bindingComponent);
    }

    @NonNull
    public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
        return inflate(inflater, DataBindingUtil.getDefaultComponent());
    }

    @NonNull
    public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable DataBindingComponent bindingComponent) {
        return bind(inflater.inflate(2131296288, (ViewGroup)null, false), bindingComponent);
    }

    // 绑定视图
    @NonNull
    public static ActivityMainBinding bind(@NonNull View view) {
        return bind(view, DataBindingUtil.getDefaultComponent());
    }

    // 验证当前布局是否为Binding指定,如果是则直接Return绑定后实体,反之抛出异常
    @NonNull
    public static ActivityMainBinding bind(@NonNull View view, @Nullable DataBindingComponent bindingComponent) {
        if (!"layout/activity_main_0".equals(view.getTag())) {
            throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
        } else {
            return new ActivityMainBinding(bindingComponent, view);
        }
    }

    // 直接实现系统点击事件
    public static class OnClickListenerImpl implements OnClickListener {
        private Presenter value;

        public OnClickListenerImpl() {
        }

        // 搞了一个实现,用于设置Value,也就是value的初始化
        public ActivityMainBinding.OnClickListenerImpl setValue(Presenter value) {
            this.value = value;
            return value == null ? null : this;
        }

        // 点击事件初始化
        public void onClick(View arg0) {
            this.value.onClick(arg0);
        }
    }
}

下面针对其中几个方法进行源码阅读分析:

  • mapBindings:遍历视图层次结构
Object[] bindings = mapBindings(bindingComponent, root, 13, sIncludes, sViewsWithIds);

这里为啥是13?是因为LZ界面中放置了1个TextView,12个Button按钮。

/**
 * 在根下遍历视图层次结构,并将标记的视图,包含视图和带有ID的视图拖放到返回的Object[]中。这用于遍历视图层以查找所有绑定和ID视图。
 *
 * @param bindingComponent 用于此绑定的绑定组件
 * @param roots 视图层次结构的根视图层级。 这与合并标签一起使用
 * @param numBindings ID'd视图的总数,包含表达式的视图和包含
 * @param includes 包含布局信息,由它们的容器索引索引
 * @param viewsWithIds 没有标签但拥有ID的视图索引
 * @return 大小为numBindings的数组包含层次结构中具有ID(在viewsWithIds中具有元素)的所有视图,都被标记为包含表达式或包含的布局的绑定。
 * @hide
 */
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View[] roots,
        int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
    Object[] bindings = new Object[numBindings];
    for (int i = 0; i < roots.length; i++) {
        mapBindings(bindingComponent, roots[i], bindings, includes, viewsWithIds, true);
    }
    return bindings;
}
  • setRootTag:设置Root标签

setTag版本兼容:

protected void setRootTag(View view) {
    if (USE_TAG_ID) { // DataBinderMapper.TARGET_MIN_SDK >= 14
        view.setTag(R.id.dataBinding, this);
    } else {
        view.setTag(this);
    }
}

SparseArray,看这个,果然,怪不得之前听说推荐使用SparseArray。有时间得看看咯。

public void setTag(int key, final Object tag) {
    // If the package id is 0x00 or 0x01, it's either an undefined package
    // or a framework id
    if ((key >>> 24) < 2) {
        throw new IllegalArgumentException("The key must be an application-specific "
                + "resource id.");
    }

    setKeyedTag(key, tag);
}

private void setKeyedTag(int key, Object tag) {
    if (mKeyedTags == null) {
        mKeyedTags = new SparseArray(2);
    }

    mKeyedTags.put(key, tag);
} 
  
  • invalidateAll:执行View异步刷新

通过调用requestRebind,执行View异步刷新。

public void invalidateAll() {
    synchronized(this) {
        this.mDirtyFlags = 2L;
    }

    this.requestRebind();
}
  • requestRebind:强制View异步刷新
protected void requestRebind() {
    // 如果bind有值,直接执行异步View更新
    if (mContainingBinding != null) {
        mContainingBinding.requestRebind();
    } else {
        // 开启同步锁
        synchronized (this) {
            if (mPendingRebind) {
                return;
            }
            mPendingRebind = true;
        }
        // 版本兼容 SDK_INT >= 16
        if (USE_CHOREOGRAPHER) {
            // 发布帧回调以在下一帧上运行 也就是View更新
            mChoreographer.postFrameCallback(mFrameCallback);
        } else {
            // 将Runnable r被添加到消息队列中。Runnable将在该处理程序所连接的线程上运行。
            mUIThreadHandler.post(mRebindRunnable);
        }
    }
}
  • postFrameCallback:发送Frame回调
// Posts a callback to run on the next frame
public void postFrameCallback(FrameCallback callback) {
    postFrameCallbackDelayed(callback, 0);
}

// 回调类型:提交回调。处理帧的绘制后操作。
    // 遍历完成后运行。 报告的{@link #getFrameTime()帧时间}
    // 在此回调期间可能会更新以反映在遍历正在进行时发生的延迟,以防重型布局操作导致某些帧被跳过。
// 在此回调期间报告的帧时间提供更好的估计动画(以及视图层次状态的其他更新)实际生效的帧的开始时间。
public static final int CALLBACK_COMMIT = 3;

private static final int CALLBACK_LAST = CALLBACK_COMMIT;

// 发布回调以在指定延迟后的下一帧上运行。回调运行一次后自动删除。
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
    if (callback == null) {
        throw new IllegalArgumentException("callback must not be null");
    }

    postCallbackDelayedInternal(CALLBACK_ANIMATION,
            callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }

    // 开启同步锁
    synchronized (mLock) {
        // 获取的是系统的时间
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        // Choreographer机制,用于同Vsync机制配合,实现统一调度界面绘图.
        // 添加队列锁
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

// 调度Frame锁定
private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "Scheduling next frame on vsync.");
            }

            // If running on the Looper thread, then schedule the vsync immediately,
            // otherwise post a message to schedule the vsync from the UI thread
            // as soon as possible.
            if (isRunningOnLooperThreadLocked()) {
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            final long nextFrameTime = Math.max(
                    mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
            if (DEBUG_FRAMES) {
                Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
            }
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        }
    }
}

不行了,晕死了,太多太多不懂了。。。

MMP呦,得赶紧开启Android重走路了。。。

结束语

原本打算挨个源码解析下,没想到看的看的发现,我日,太多东西了,想一篇文章搞定?对于目前的LZ而言,太过于困难。与其继续死抠,不如赶紧加强基础,练好基本功,再来攻克Boss。

你可能感兴趣的:(Android,Love,and,Hatred)