在项目开发中,我们可能为了获取一个控件的尺寸,通过view.post方法去获取这个控件的大小
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
test_wh.post {
Log.d("shixiangyu", test_wh.width.toString())
Log.d("shixiangyu", test_wh.height.toString())
}
}
每次都好像屡试不爽,但真的就准确吗,再得出答案前,需要搞明白view.post方法做了些什么
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {//onCreate时,attachInfo还为空
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
我们在Activity的onCreate方法调用view.post时,attachInfo还为空(为什么空,可以参考另外一篇文章),所以,我们看看getRunQueue().post(action)
:
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
getRunQueue获得当前View类对象中的一个全局变量mRunQueue,然后把我们传进来的Runnable交给了mRunQueue,继续跟进HandlerActionQueue的post方法
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
GrowingArrayUtils的append方法:
public static <T> T[] append(T[] array, int currentSize, T element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray(
(Class<T>) array.getClass().getComponentType(), growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
看到这,我们明白,View中有一个HandlerActionQueue类型全局变量mRunQueue,mRunQueue持有一个HandlerAction[]数组,用来存放我们post进来的Runnable,到此,似乎就结束了,那么什么时候去处理这些Runnable呢?很简单,只需要看看这个mRunQueue什么时候被使用就可以了,通过查找,我们在View的dispatchAttachedToWindow
方法中知道了对它的使用:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
// ...... 无关代码删减
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
}
// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.
// As all views in the subtree will already receive dispatchAttachedToWindow
// traversing the subtree again here is not desired.
onVisibilityChanged(this, visibility);
}
dispatchAttachedToWindow方法中调用了mRunQueue.executeActions方法,而executeActions方法终于把我们的Runnable交给了我们熟悉的Handler
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
所以,我们需要知道View的dispatchAttachedToWindow方法何时何地被调用,这里不再对此详细阐述,可以看另外一篇文章,dispatchAttachedToWindow在ViewRootImpl的performTraversals()方法中被首次调用:
private void performTraversals() {
// .......省略代码
final View host = mView;
host.dispatchAttachedToWindow(mAttachInfo, 0);
}
其中host就是DecorView,我们知道DecorView继承自FrameLayout,而FrameLaylat继承自ViewGroup,我们看看ViewGroup的dispatchAttachedToWindow方法:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
super.dispatchAttachedToWindow(info, visibility);
mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
View view = mTransientViews.get(i);
view.dispatchAttachedToWindow(info,
combineVisibility(visibility, view.getVisibility()));
}
}
一层层调用子View的dispatchAttachedToWindow方法。看到这,我们可以捋一捋整个逻辑了:
当我们调用了View.post(Runnable action)方法后,我们的action并没有直接进行交给Handler,而是暂存到了View的全局变量mRunQueue中,等到ViewRootImpl对View和window进行关联时才取出View中暂存在mRunQueue的任务,交给Handler,
网友评论