LZ-Says:洗个澡,突然感觉爽到爆~~~ 又回来了哦~
前几天,终于完善了关于Data Binding基础篇以及进阶篇博文编写,过程很是艰难哦~
下面附上链接地址:
Android Study 之 如何通过Data Binding提升撸码逼格(基础篇);
Android Study 之 如何通过Data Binding提升撸码逼格(进阶篇);
而今天,整整行囊,准备开启Data Binding高级篇,完成之后,也该开启Android重新回味之路了,长线计划,一定要缩短时间咯~~~
拖了好久咯~~~ MMP呦~
以下关于Data Binding分析基于Libary 1.3.1。
首先我们来回顾下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布局层级关系图:
可以很清晰的看到,在我们的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这里就不过多的阐述了。
上述讲述了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一样了。
一个相当于直接引用布局,直接拿到了布局的控制权,而另一个则是需要去获取上级,通过获取上级拿到控制权进行操作。
怎么看源码呢?
在Android Studio 3.1.1中,可以按照下图所示查看:
首先看一波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);
}
}
}
下面针对其中几个方法进行源码阅读分析:
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;
}
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
通过调用requestRebind,执行View异步刷新。
public void invalidateAll() {
synchronized(this) {
this.mDirtyFlags = 2L;
}
this.requestRebind();
}
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);
}
}
}
// 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。