为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Android

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

图片描述

前言

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

下面附上链接地址:

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

拖了好久咯~~~ MMP呦~

启程

图片描述

以下关于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 extends ViewDataBinding> T setContentView(Activity activity, int layoutId) {        return setContentView(activity, layoutId, sDefaultComponent);
    }

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

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

    public static <T extends ViewDataBinding> 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布局层级关系图:

图片描述

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

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

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

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

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

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

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

下面继续深入bind方法。

/**
 * Returns the binding for the given layout root or creates a binding if one
 * does not exist.
 * <p>
 * Prefer using the generated Binding's <code>bind</code> method to ensure type-safe inflation
 * when it is known that <code>root</code> 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 extends ViewDataBinding> 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 extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,        int layoutId) {    return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}static <T extends ViewDataBinding> 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方法中具体执行了什么操作:

/**
 * 返回一个绑定后的根布局或者创建一个不存在的绑定结果
 * 当已知<code>root</code>尚未绑定时,优先使用生成的Binding的<code>bind</code>方法来确保类型安全的范围
 * @param root 根布局是引入的绑定的Layout
 * @param bindingComponent 用于数据绑定的DataBindingComponent
 * @return 指定根视图的ViewDataBinding。如果已经存在,现有的将被返回
 * @throws 当引入Layout不输入绑定类型则抛出IllegalArgumentException
 * @see #getBinding(View)
 */@SuppressWarnings("unchecked")public static <T extends ViewDataBinding> 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这里又是什么鬼?

/**
 * 检索负责给定视图布局根的绑定。如果没有绑定,则返回<code>null</code>,否则将DataBindingComponent设置成默认:{@link #setDefaultComponent(DataBindingComponent)}
 *
 * @param view 具有绑定的布局中的根<code>View</code>
 * @return 如果不是bind视图或者没有进行关联绑定直接返回null
 */public static <T extends ViewDataBinding> 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)}.
 * 
 * 除非<code>layoutId</code>是未知的,才使用此版本。否则,使用生成的绑定的引用方法来确保类型安全的引用
 *
 * @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 <code>null</code> 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 extends ViewDataBinding> 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.
 * <p>
 * Use this version only if <code>layoutId</code> 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 <code>null</code> if
 * the layoutId wasn't for a binding layout.
 * @throws InflateException When a merge layout was used and attachToParent was false.
 */public static <T extends ViewDataBinding> 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类源码阅读

怎么看源码呢?

首先看一波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;// 继承ViewDataBindingpublic 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<Object>(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 framepublic 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。

原文链接:http://www.apkbus.com/blog-904057-77930.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消