为何在onCreate中通过View.post能获取宽高
Posted yuminfeng728
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为何在onCreate中通过View.post能获取宽高相关的知识,希望对你有一定的参考价值。
我们在获取View的宽高时,其实执行的代码是:
/**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth()
return mRight - mLeft;
/**
* Return the height of your view.
*
* @return The height of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight()
return mBottom - mTop;
而我们知道 mRight,mLeft,mTop,mBottom 四个值是在View执行完layout 方法时才会被赋值,而layout方法是由ViewRootImpl中performLayout发起调用的。但是我们也知道ViewRootImpl调用performLayout方法是在Activity执行完onResume方法之后才开始执行的。这里就出现了题目中提出的View.post是如何使得在onCreate方法中也能获得View的宽高。
我们首先进入View.post的方法中:
/**
* <p>Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread.</p>
*
* @param action The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* @see #postDelayed
* @see #removeCallbacks
*/
public boolean post(Runnable action)
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null)
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;
注释中已经说明,将一个Runnable加入到消息队列中来,并且runnable将在用户界面线程上运行。方法中首先对attachInfo 判断是否为空,这里我们之前分析过,知道mAttachInfo对象其实是在dispatchAttachedToWindow方法中初始化的,就在ViewRootImpl的performTraversals方法中执行。这里直接执行下面getRunQueue().post(action)。
/**
* Returns the queue of runnable for this view.
*
* @return the queue of runnables for this view
*/
private HandlerActionQueue getRunQueue()
if (mRunQueue == null)
mRunQueue = new HandlerActionQueue();
return mRunQueue;
如果为空,则为View实例化一个runnable队列。我们可以分析一下这个类:
HandlerActionQueue
public class HandlerActionQueue
private HandlerAction[] mActions;
private int mCount;
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++;
public void removeCallbacks(Runnable action)
synchronized (this)
final int count = mCount;
int j = 0;
final HandlerAction[] actions = mActions;
for (int i = 0; i < count; i++)
if (actions[i].matches(action))
// Remove this action by overwriting it within
// this loop or nulling it out later.
continue;
if (j != i)
// At least one previous entry was removed, so
// this one needs to move to the "new" list.
actions[j] = actions[i];
j++;
// The "new" list only has j entries.
mCount = j;
// Null out any remaining entries.
for (; j < count; j++)
actions[j] = null;
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;
public int size()
return mCount;
public Runnable getRunnable(int index)
if (index >= mCount)
throw new IndexOutOfBoundsException();
return mActions[index].action;
public long getDelay(int index)
if (index >= mCount)
throw new IndexOutOfBoundsException();
return mActions[index].delay;
private static class HandlerAction
final Runnable action;
final long delay;
public HandlerAction(Runnable action, long delay)
this.action = action;
this.delay = delay;
public boolean matches(Runnable otherAction)
return otherAction == null && action == null
|| action != null && action.equals(otherAction);
post里面执行的postDelayed方法,然后创建一个大小为4的数组,并且将保存Runnable对象的HandlerAction对象存储到数组中。
到这里一个Runnable任务就保存了起来,但是这里并没有立即执行Runnable任务,那么到底在哪里执行的呢?
如果对前面分析界面绘制的文章有印象的话,应该知道是在ViewRootImpl中performTraversals执行的:
ViewRootImpl#performTraversals
private void performTraversals()
// cache mView since it is used so much below...
final View host = mView;
if (host == null || !mAdded)
return;
.....
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
.....
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
.....
performLayout(lp, mWidth, mHeight);
.....
performDraw();
这里我们看到了执行executeActions方法,但是它竟然先于performMeasure方法执行,这下心中一紧,如果这样的话那么通过View.post()方式获取的应该是还没有测量过的宽高啊,应该没有值才对。我们这里先保留这个问题。
先看HandlerActionQueue中的executeActions方法:
HandlerActionQueue#executeActions
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;
遍历数组中存储的Runnable任务,然后通过handler来执行,由handler来post这个Runnable对象,于是这个Runnable任务又被加入到Handler中的MessageQueue中。我们知道ViewRootImpl的Handler就是主线程的Handler,而performTraversals()所在的Runnable最后会被添加到同一个主线程中的looper的MessageQueue中。由于是Handler中的消息驱动模式,需要等待主线程的Handler执行完当前的任务(即performTraversals),才会去执行我们View.post的那个Runnable。如此才避免了executeActions中的runnable在performLayout方法之前调用。
到这里,我们便解释了为何在onCreate中通过View.post能获取宽高值。
以上是关于为何在onCreate中通过View.post能获取宽高的主要内容,如果未能解决你的问题,请参考以下文章