我们都知道,如果直接在onCreate()去获取一个控件的宽高,是获取不到的,需要在onCreate()里面直接调用view.post()或者view.postDelayed去获取。
btn.post(new Runnable() {
@Override
public void run() {
btn.getWidth();
}
});
btn.postDelayed(new Runnable() {
@Override
public void run() {
}
},100);
下面我们来具体看看post和postDelayed具体怎么操作。
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
在post的方法中分两种情况,如果AttachInfo不为null,则直接调用handler的post(),否则调用HandlerActionQueue的post()。一开始进来AttachInfo是为null的,那就会走下面的逻辑。
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++;
}
}
HandlerActionQueue的post()的将传过来的runnable封装成一个HandlerAction放在HandlerAction数组中。好像从这里并不能发现什么,那回过头去看AttachInfo什么时候不为null,也就是什么时候赋值。全局搜索可知,在dispatchAttachedToWindow()进行赋值。
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
....
}
从上面可以知道,赋值完成之后,然后判断mRunQueue是否为null,如果不为null,则调用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放到mAttachInfo中的handler中。而mAttachInfo中的handler是在ViewRootImpl的构造函数中进行赋值,
final ViewRootHandler mHandler = new ViewRootHandler();
那什么时候会调用这个方法?在哪里调用?接下来看AttachInfo.全局搜索是在ViewRootImpl里面调用,在ViewRootImpl的构造函数里会创建一个mAttachInfo对象,然后在performTraversals()调用
dispatchAttachedToWindow()。我们都知道,在 ViewRootImpl 的 performTraversals 方法,在该方法将会依次完成 View 绘制流程的三大阶段:测量、布局和绘制。
// View 绘制流程开始在 ViewRootImpl
private void performTraversals() {
// mView是DecorView
final View host = mView;
if (mFirst) {
.....
// host为DecorView
// 调用DecorVIew 的 dispatchAttachedToWindow,并且把 mAttachInfo 给子view
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
.....
}
mFirst=false
...
getRunQueue().executeActions(mAttachInfo.mHandler);
// View 绘制流程的测量阶段
performMeasure();
// View 绘制流程的布局阶段
performLayout();
// View 绘制流程的绘制阶段
performDraw();
...
}
从上面可知,dispatchAttachedToWindow是在performMeasure()的前面执行,它又是怎么保证在测量后才得到器宽高的。
那我们得看view的绘制流程。在调用performTraversals的前面会调用scheduleTraversals()。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
在scheduleTraversals()会添加一个mTraversalBarrier,这个作用就是添加一个栅栏,让异步的消息优先执行,而同步消息挡在栅栏外面。
这时会调用Choreographer的postCallback(),并将mTraversalRunnable作为参数传进去。而mTraversalRunnable是后续的View的绘制任务。
下面来看一下postCallback(),
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, 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;
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);
}
}
}
postCallback最终调用的是postCallbackDelayedInternal(),特别注意一下postCallback方法的第三个参数,后面需要根据这个token做判断。
在postCallbackDelayedInternal根据时间来判断如果延时小于now,则执行scheduleFrameLocked(),否则设置成异步任务,然后通过handler发送到队列当中去。
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
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);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
先判断当前looper是不是同一个,是同一个则调用scheduleVsyncLocked()。否则设置成一个异步消息。
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
private static native void nativeScheduleVsync(long receiverPtr);
最终调用native方法去请求Vsync信号。得到Vsync信号成功之后,会回调onVsync()。
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
long now = System.nanoTime();
if (timestampNanos > now) {
timestampNanos = now;
}
if (mHavePendingVsync) {
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
通过Handler发送了一条消息,执行了本身的Runnable回调方法,也就是doFrame()。
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
}
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
上面的代码做了两件事:
1、开始绘制要在vsync信号来的时候开始,保证两者时间对应。所以如果时间没对上,就是发送了跳帧,那么就要修正这个时间,保证后续的时间对应上。
2、执行所有的Callback任务。
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
if (jitterNanos >= 2 * mFrameIntervalNanos) {
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
+ mFrameIntervalNanos;
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
doFrame处理的时间有五种,分别为Choreographer.CALLBACK_INPUT,Choreographer.CALLBACK_ANIMATION、Choreographer.CALLBACK_INSETS_ANIMATION、Choreographer.CALLBACK_TRAVERSAL、Choreographer.CALLBACK_COMMIT。在doFrame()会分别去执行对应的时间,调用其run方法。
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
这个token在前面提到过,从scheduleTraversals发送过来的Runnable会定义为Choreographer.CALLBACK_TRAVERSAL类型的时间,并且token为null.所以上面的代码会走else.而这个action就是传进来的mTraversalRunnable,会调用mTraversalRunnable的run方法。
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
TraversalRunnable里面调用doTraversal(),doTraversal()里面就看到我们熟悉的view的绘制流程方法。因此,这个时候Handler正在执行着TraversalRunnable这个Runnable,而我们post的Runnable要等待TraversalRunnable执行完才会去执行,而TraversalRunnable这里面又会进行measure,layout和draw流程,所以等到执行我们的Runnable时,此时的View就已经被measure过了,所以获取到的宽高就是measure过后的宽高。
网友评论