View从setContentView
到onMeasure->onLayout->onDraw
经历哪些流程。下面就来梳理哈View的绘制流程
setContentView做了什么工作?
Activity.setContentView
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
这里调用PhoneWindow
的 setContentView
,然后在初始化ActionBar
PhoneWindow.setContentView
public void setContentView(int layoutResID) {
...
//第一次调用这里必为空
if (mContentParent == null) {
//创建DecorView,并将ContentParent区域的View对象赋值给mContentParent
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
创建DecorView
,在创建DecorView
时也将Window
设置DecorView
LayoutInflater.inflate
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//预加载的View,比会返回null
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
//得到资源解析器
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
//处理merge标签
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// 创建本层级View,也就是xml中最外层的ViewGroup,并加入到ContentParent中
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// 递归创建之View,并添加到他的父View中
rInflateChildren(parser, temp, attrs, true);
}
...
return result;
}
}
createViewFromTag:方法创建xml中最外层父ViewGroup
,将该View
添加至ContentParent
中,并设置ViewGroup
的LayoutParams
.
rInflateChildren:使用递归的方法去创建子View
,并添加到他们的父View
中
这样setContentView
的工作就结束了,setContentView
的工作加载xml
解析后生成View
,填充到DecorView
的ContentParent
中。将xml
资源转为对象,并填充至DecorView
中。
下面我们继续看View的绘制
View绘制
在上一篇《Activity启动流程-基于Android API31》中有介绍到View在ActivityThread.handleResumeActivity中进行绘制
ActivityThread.handleResumeActivity
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
...
//执行onResume方法
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
...
//添加DecorView
if (r.window == null && !a.mFinished && willBeVisible) {
...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//添加decor,并使用Handler发送异步消息,用于View绘制
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
...
}
WindowManagerImpl.addView
WindowManager的实现类
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
//赋值windowToken, PopupDialog多次添加同一个PopupDialog时提示token问题就是这个
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
WindowManagerGlobal.addView
是一个单例,管理ViewRootImpl
、RootView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
创建ViewRootImpl实例
root = new ViewRootImpl(view.getContext(), display);
//设置LayoutParams
view.setLayoutParams(wparams);
//WindowManagerGlobal 记录信息
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//DecorView设置给ViewRootImpl
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
ViewRootImpl.setView
View的顶层类。WindowManagerGlobal
为Window提供View管理,WindowManagerGlobal
管理所有的ViewRoot
、ViewRootImpl
。ViewRootImpl
更像是WindowManagerGlobal
的具体实现类。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
....
requestLayout();
...
//IPC 通信,这里将ViewRootImpl的通讯句柄交给WMS,就是这里传入的mWindow,mWindow类型是ViewRootImpl的内部类M,用于WMS对ViewRootImpl的通讯,mWindowSession用于ViewRootImpl对WMS的通讯
//这里的IWindowSession实现类为“com.android.server.wm.Session”,最终方法内调用WindowManagerService.addWindow添加window
//res,返回添加状态详细参考WindowManagerGlobal.ADD_xxxxx
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
mTempControls);
...
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//检测主线程,不在就抛异常
checkThread();
mLayoutRequested = true;
//开启调度
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//发送屏障消息,为绘制做准备
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//添加绘制回调
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
- 第一步发送消息屏障
- 第二步添加VSYNC信号监听(以60帧率计算 1s/60 最大16.6ms刷新一次),当VSYNC信号来时回调
mTraversalRunnable
消息同步屏障:postSyncBarrier()
发送同步屏障消息,这个消息会使Handler停止处理同步普通消息,只处理异步消息。发送屏障消息时,如果Handler处于阻塞状态也不会去唤醒。
异步消息:普通message调用setAsynchronous(true)
,即可成为异步消息,当设置了同步屏障后,优先同步消息执行
如果未发送屏障消息,异步消息和同步消息一样,不会获得优先执行的权利。
这里设置同步屏障,因为马上要进行界面绘制,避免其他耗时消息导致绘制延迟。
Choreographer.postCallback
这个类对上层应用提供VSYNC信号监听(FrameDisplayEventReceiver
),当有信号量时处理调监听回调。对底层起到接收VSYNC信号的作用(FrameDisplayEventReceiver
)
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);
//延迟时间是否到了,一般情况都是true
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);
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {//true
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
//当前线程是否和looper线程一致
if (isRunningOnLooperThreadLocked()) {
//请求信号量
scheduleVsyncLocked();
} else {
//不一致用Handler切换到Looper线程
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
//老版本使用Handler去处理绘制,新版本使用VSYNC信号
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);
}
}
}
private void scheduleVsyncLocked() {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");
//请求信号量,实际调用 DisplayEventReceiver.nativeScheduleVsync(mReceiverPtr);
mDisplayEventReceiver.scheduleVsync();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
Choreographer.FrameDisplayEventReceiver
Choreographer的内部类继承DisplayEventReceiver
用于监听信号量回调,当注册后,有了信号量会回调FrameDisplayEventReceiver.dispatchVsync
方法,该方法为重写所以会调用父类DisplayEventReceiver.dispatchVsync
这里有用到2个同步消息的地方,一个为了切换线程,一个是老版本的利用Handler异步消息去绘制界面
FrameDisplayEventReceiver.dispatchVsync
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
...
// TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#onVsync " + vsyncEventData.id);
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
mLastVsyncEventData = vsyncEventData;
//发送同步消息,Callback为this。
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
@Override
public void run() {
//消息回调执行这里
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
}
这里为什么使用Handler去异步消息后执行run
方法,不直接调用run
方法呢?应该也是为了切换线程
Choreographer.doFrame
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
...
//掉帧日志
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= frameIntervalNanos) {
final long skippedFrames = jitterNanos / frameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % frameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (frameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
traceMessage("Frame time goes backward");
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
traceMessage("Frame skipped due to FPSDivisor");
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
mLastVsyncEventData = vsyncEventData;
}
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
//输入事件回调处理
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
mFrameInfo.markAnimationsStart();
//动画回调处理
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
frameIntervalNanos);
mFrameInfo.markPerformTraversalsStart();
//消息绘制处理
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
//Commit 相关回调
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
//根据回调类型,取出所有回调
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
...
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));
}
//执行run方法
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);
}
}
- 取出回调并执行
run
方法 - 回收回调对象。因为绘制(包括绘制在内的,动画,输入事件等回调事件超级多)非常超级频繁,如果一直创建和回收会造成内存抖动。
这里的run
方法为Choreographer.postCallback
方法传入的TraversalRunnable
对象
TraversalRunnable.run
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
ViewRootImpl.performTraversals
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//删除屏障信息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//绘制
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
删除屏障信息,让Handler获得继续处理同步消息的能力
ViewRootImpl.performTraversals
private void performTraversals() {
...
//window的宽高
int desiredWindowWidth;
int desiredWindowHeight;
...
//根据window宽高去测试View树,是否需要修改Window,这个方法中最少执行一次onMeasure,至多执行三次
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
...
//重新设置Window,并且关联surface
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
//再次测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
if (didLayout) {
//调用Layout
performLayout(lp, mWidth, mHeight);
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw) {
//调用Draw
performDraw();
}
}
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
"Measuring " + host + " in display " + desiredWindowWidth
+ "x" + desiredWindowHeight + "...");
//是否测量好
boolean goodMeasure = false;
//宽度为WRAP_CONTENT
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
+ ", desiredWindowWidth=" + desiredWindowWidth);
//window的宽度是否大于基础尺寸
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
//第一次测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight()
+ ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
+ " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
//测试的尺寸合适
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
//子view想要更大的空间
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
//尺寸不合适,再次测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
//第三次测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after measure");
host.debug();
}
return windowSizeMayChange;
}
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
//RootView 希望填充Window,则满足它,此时它尺寸是确切值
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
//RootView 希望根据自身内容来确定尺寸,则设置为AT_MOST 模式
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
//RootView 希望直接指定尺寸值,则满足它,此时它尺寸是确切值
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
//根据测量的view树的值,重新设置window大小
int relayoutResult = mWindowSession.relayout(mWindow, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize);
mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
//关联Window和surface
mSurface.copyFrom(mSurfaceControl);
} else {
final Surface blastSurface = getOrCreateBLASTSurface();
// If blastSurface == null that means it hasn't changed since the last time we
// called. In this situation, avoid calling transferFrom as we would then
// inc the generation ID and cause EGL resources to be recreated.
if (blastSurface != null) {
mSurface.transferFrom(blastSurface);
}
}
...
///RootView记录window大小
setFrame(mTmpFrames.frame);
...
return relayoutResult;
}
首先获取到window的宽高,然后使用measureHierarchy去测试ViewTree,看是否需要重新设置window大小。
measureHierarchy三次测量
- 先用内置的基础值测量ViewTree
- ViewTree要得更大。于是加大基础值,再次测量
- 第二次测量还是不满足ViewTree,这次使用window的最大宽度再次测量
设置Window的宽高为当天ViewTree
的宽高,并关联surface
重新设置好Window后再次测量,然后调用layout
和draw
绘制。
总结
setContextVIew:将xml资源转化成View对象
onMeasure:测量View大小
onLayout:确定View位置
onDraw:绘制View对象
实际上onDraw后我们仍然是看不到图形的,因为onDraw方法只是将View对象转成一个buffer对象,需要SurfaceFlinger
合成,发送至屏幕,才可以见到真正的图片。
View对象是如何转成buffer
的呢,这里就会用到上面绑定Surface
-
Surface
通过dequeueBuffer
获取到一块绘制缓冲区 -
Canvas
在draw
时会调用底层Skia
引擎进行数据写入 -
Surface
通过queueBuffer
提交数据到SurfaceFlinger
-
SurfaceFlinger
接受区对这些数据进行合,发送到屏幕显示
SurfaceFlinger:接受缓冲区,对它们进行合并,然后发送到屏幕显示