View的繪制過(guò)程
- Q:知道了activity_main.xml是如何添加到DecorView的,那這個(gè)DecorView是如何添加到Window的呢?
- 簡(jiǎn)單回顧下Phonewindow#setCountView的過(guò)程
1.創(chuàng)建好DecorView
2.先確定好activtiy的父布局即mContentParent是 #generateLayout得到的
3.在generateLayout方法中通過(guò)判斷確定好layoutResource(系統(tǒng)預(yù)設(shè)的xml布)局
4.找到在layoutResource中id為com.android.internal.R.id.content的ViewGroup賦值給mContentParent
5.layoutResource通過(guò)mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)方法layoutResource把添加進(jìn)了DecorView
6.最后將activity_main添加到mContentParent,到這里就結(jié)束了整個(gè)流程,并沒(méi)有將DecorView添加到window的步驟。 - 關(guān)于LayoutInflater#inflate
- 大體可以理解將xml解析出來(lái),通過(guò)LayoutInflater#createViewFromTag方法根據(jù)名稱反射實(shí)例化view后添加到ViewGroup root,子view添加通過(guò)rInflateChildren循環(huán)找到view添加到ViewGroup
正式開始分析
- 直接進(jìn)入ActivityThread#handleResumeActivity
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
//這個(gè)步驟去掉nResume方法
//->r.activity.performResume ->
//--> mInstrumentation.callActivityOnResume
//這也就是onResume是頁(yè)面可見了的說(shuō)法了
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
if (r == null) {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
if (mActivitiesToBeDestroyed.containsKey(token)) {
// Although the activity is resumed, it is going to be destroyed. So the following
// UI operations are unnecessary and also prevents exception because its token may
// be gone that window manager cannot recognize it. All necessary cleanup actions
// performed below will be done while handling destruction.
return;
}
final Activity a = r.activity;
if (localLOGV) {
Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity
+ ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
}
final int forwardBit = isForward
? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//r.window 都是在handleLaunchActivity方法中添加中進(jìn)去的
//第一次啟動(dòng)這個(gè)activity這個(gè)r.window就是null所以進(jìn)入下面的判斷進(jìn)行添加
if (r.window == null && !a.mFinished && willBeVisible) {
//拿到phoneWindow
r.window = r.activity.getWindow();
//拿到phoneWindow的DecorView
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//關(guān)鍵在這個(gè)addview 將decorview添加到window里面去
//Window#通過(guò)兜兜轉(zhuǎn)轉(zhuǎn)找到wm就是WindowManagerImpl
//-》 ViewManager wm = a.getWindowManager();
//Activity#attach方法--》mWindowManager = mWindow.getWindowManager();
//Window#setWindowManager---》mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
wm.addView(decor, l);
} else {
//....
}
}
//..
}
- 進(jìn)入WindowManagerImpl#addView,這里又調(diào)用了WindowManagerGlobal#addView
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
//設(shè)置默認(rèn)token
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
- 進(jìn)入WindowManagerGlobal#addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
//...省略
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//...省略
//創(chuàng)建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
//給DecorView設(shè)置LayoutParams信息
view.setLayoutParams(wparams);
//保存DecorView
mViews.add(view);
//保存ViewRootImpl
mRoots.add(root);
//保存WindowManager.LayoutParams
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//ViewRootImpl#setView
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
- 進(jìn)入ViewRootImpl#setView了,篇幅太長(zhǎng)只保留了關(guān)鍵的代碼requestLayout()和mWindowSession.addToDisplayAsUser
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
//..省略一堆設(shè)置過(guò)程
// any other events from the system.
requestLayout();
//...省略
try {
mOrigWinpatibility(mWindowAttributes);
//將窗口添加到WMS上面 這里打個(gè)標(biāo)記留意下
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
inputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
//這里給DecorView設(shè)置mParent
view.assignParent(this);
//...省略
}
}
}
- ViewRootImpl#requestLayout()方法,requestLayout()這個(gè)一下子就熟悉起來(lái)了,自定義view的時(shí)候應(yīng)該都用到過(guò)特別是viewgroup,實(shí)際最后調(diào)用的方法就是這里了,先丟個(gè)坑在這,后續(xù)講解下
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
- checkThread這有個(gè)經(jīng)典的問(wèn)題,為啥不能在子線程更新ui。
- 這里判斷了當(dāng)前ViewRootImpl是不是在同一個(gè)線程執(zhí)行,不是就拋出異常,而ViewRootImpl剛好是在主線程創(chuàng)建的#Activity#main方法創(chuàng)建了主線程->Looper.prepareMainLooper();,剛好handleResumeActivity創(chuàng)建ViewRootImpl就是在主線程,嚴(yán)格意義上來(lái)說(shuō),是不能不同線程更新ui。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
- ViewRootImpl#scheduleTraversals,接著分析scheduleTraversals
- 這樣也比較眼熟,網(wǎng)上找屏幕刷新機(jī)制都會(huì)提到這個(gè)。這個(gè)方法是屏幕刷新最重要的方法,推薦個(gè)相關(guān)文章
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//這里創(chuàng)建了一個(gè)同步屏障消息
//[關(guān)于同步屏障](https://blog.csdn.net/start_mao/article/details/98963744)
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
- 回到正文這里,這里傳入了一個(gè)Runnable,所以最終會(huì)掉到這個(gè)doTraversal()
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
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");
}
//重點(diǎn)這里
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
- 看看這個(gè)performTraversals方法
private void performTraversals() {
//代碼太多了 只保留關(guān)鍵代碼
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
//...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//...
performLayout(lp, mWidth, mHeight);
//...
performDraw()
//...
}
-
measureHierarchy 這里進(jìn)行了3次預(yù)測(cè)量,當(dāng)view的寬度設(shè)置為WRAP_CONTENT時(shí)會(huì)預(yù)測(cè)量2次
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;
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);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
//如果給的desiredWindowWidth比得到baseSize大 那就說(shuō)明尺寸不行 給一個(gè)值測(cè)量下
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
//第一次測(cè)量
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 {
//第一次給的尺寸還不行繼續(xù)測(cè)量
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
//第二次測(cè)量
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) {
//如果還不滿意就繼續(xù)最后一次測(cè)量
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
//第三次測(cè)量
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;
}
- 根據(jù)上面3次的測(cè)量條件可以在加上下面的一次最多測(cè)量4次 最少測(cè)量2次,按照預(yù)測(cè)的方法來(lái)說(shuō),將width不要設(shè)置成WRAP_CONTENT會(huì)快一點(diǎn)(手動(dòng)狗頭)
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//這里會(huì)執(zhí)行mView.measure->在執(zhí)行View#onMeasure()方法
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
- 這里mView.measure實(shí)際上是DecorView,所以onMeasure的時(shí)候?qū)嶋H調(diào)用的是DecorView#onMeasure然后DecorView繼承自FrameLayout,先通過(guò)自己寬高模式啥的的一番計(jì)算調(diào)用了 super.onMeasure(widthMeasureSpec, heightMeasureSpec);所以直接去看FrameLayout的onMeasure
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
boolean optical = isLayoutModeOptical(this);
//這里去onMeasure是有個(gè)前提的 強(qiáng)制繪制布局或者需要刷新布局
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
//這里看看緩存的數(shù)量小于0或者忽略緩存(小于sdk19都會(huì)忽略緩存)
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
//真正測(cè)量
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
//如果有緩存
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
//這里會(huì)拋個(gè)異常,重寫onMeasure方法需要執(zhí)行setMeasuredDimension或者super.onMeasure就會(huì)拋異常,因?yàn)樵趕etMeasuredDimension設(shè)置了mPrivateFlags,不設(shè)置這個(gè)flags就會(huì)進(jìn)入這個(gè)異常
throw new IllegalStateException("View with id " + getId() + ": "
+ getClass().getName() + "#onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
//添加到緩存列表
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
//這里是FrameLayout的onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//...通過(guò)子view的Margin去累加計(jì)算出最大寬高值
}
}
//...省略計(jì)算容器寬高過(guò)程
//設(shè)置容器自己的寬高
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
//...省略通過(guò)子view的getLayoutParams計(jì)算寬高設(shè)定子view大小
//通過(guò)MATCH_PARENT判斷給容器的寬高或者通過(guò)getChildMeasureSpec方法去計(jì)算
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
- 上門就是測(cè)量的過(guò)程,先預(yù)測(cè)量寬高,不管結(jié)果如何再去測(cè)量一次確定一下啊,然后循環(huán)去調(diào)用子view的測(cè)量
- 測(cè)量看完,看看performLayout,這里思路和上面測(cè)量一樣的
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
if (host == null) {
return;
}
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
//就這里一個(gè)重點(diǎn)
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//..
}
//..
}
- host就是DecorView而他繼承了FrameLayout,F(xiàn)rameLayout就是viewGroup
- viewGroup#layout->View#layotu->View#onLayout
- 所以最終執(zhí)行的是FrameLayout#onlayout
//view#layout
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//這里setFrame把自己的位置擺好
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
//...
}
//...
}
//View#setFrame
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
//...
//這里提一嘴 如果布局發(fā)生了改變就會(huì)執(zhí)行invalidate觸發(fā)onDraw
// Invalidate our old position
invalidate(sizeChanged);
//..
}
return changed;
}
//FrameLayout#onlayout
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
//FrameLayout#layoutChildren
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
//循環(huán)去調(diào)用了子view的layout
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
//...省略計(jì)算Margin、pading部分
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
- 這里繪制流程和布局流程都是一個(gè)套路,看看performDraw
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
//...
try {
//關(guān)鍵代碼
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
//...
}
//ViewRootImpl#draw
private boolean draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
//...
mAttachInfo.mTreeObserver.dispatchOnDraw();
//...
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
//...
//1.這里會(huì)最終會(huì)走view.onDraw
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
//2.這里會(huì)最終會(huì)走view.onDraw
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return useAsyncReport;
}
- 上面ViewRootImpl#draw方法打了2個(gè)注釋會(huì)走view.onDraw方法,先分析第一個(gè)
//ThreadedRenderer#draw
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
//..重點(diǎn)這個(gè) 這個(gè)方法主要走了 updateViewTreeDisplayList(view)
//
updateRootDisplayList(view, callbacks);
//...
}
//省點(diǎn)篇幅 直接跳到updateViewTreeDisplayList
private void updateViewTreeDisplayList(View view) {
view.mPrivateFlags |= View.PFLAG_DRAWN;
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
//主要看這里此時(shí)的view還是DecorView
view.updateDisplayListIfDirty();
view.mRecreateDisplayList = false;
}
//DecorView和FrameLayout以及viewgroup都沒(méi)有對(duì)updateDisplayListIfDirty方法重寫,所以直接去看View里面的
//View#updateDisplayListIfDirty
public RenderNode updateDisplayListIfDirty() {
//...
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
if (layerType == LAYER_TYPE_SOFTWARE) {
//關(guān)閉了硬件加速
//直接把view繪制成bitmap
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
//有SKIP_DRAW這個(gè)就是有沒(méi)有設(shè)置背景色沒(méi)有就直接去繪制子view
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
} else {
//重點(diǎn)
draw(canvas);
}
}
} finally {
renderNode.endRecording();
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
- 去看看draw方法干啥了
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
* 7. If necessary, draw the default focus highlight
*/
// Step 1, draw the background, if needed
int saveCount;
drawBackground(canvas);
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
//..
}
- onDraw這個(gè)就不用說(shuō)了,看下dispatchDraw怎么繪制子view的,dispatchDraw是抽象的方法所以先找下DecorView和FrameLayout有沒(méi)有實(shí)現(xiàn),沒(méi)有找到所以直接接著找ViewGroup
@Override
protected void dispatchDraw(Canvas canvas) {
//..
for (int i = 0; i < childrenCount; i++) {
//...
//這里循環(huán)去調(diào)用了子view的draw方法
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
}
//drawChild
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
- 上面ViewRootImpl#draw提到調(diào)用drawSoftware的方法,一樣的套路
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
//...
try {
//...
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
//..
}
return true;
}
- 到這里測(cè)量繪制的流程就完結(jié)了,setview中mWindowSession.addToDisplayAsUser就是把window添加到wms顯示了。通過(guò)view.assignParent(this);給DecorView設(shè)置了父類,所以說(shuō)ViewRootImpl是整個(gè)界面的爸爸。
分析下requestLayout和invalidate
當(dāng)調(diào)用xxxView#requestLayout的時(shí)候會(huì)發(fā)生什么
//View#requestLayout
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
//重點(diǎn)在這
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
- 看到這個(gè)重點(diǎn)就有一個(gè)遞歸的過(guò)程了,上面分析知道了ViewRootImpl是整個(gè)頁(yè)面的爸爸,所以這個(gè)重點(diǎn)代碼其實(shí)就是遞歸找ViewRootImpl并執(zhí)行requestLayout方法了。
- 可以抽象看成:子view找爸爸viewgroup->viewgroup(DecorView)找爸爸ViewRootImpl,在讓這個(gè)最后的爸爸執(zhí)行requestLayout方法
- 至于requestLayout上面就有分析了不多重復(fù)了,有一個(gè)注意的一點(diǎn)的是不一定會(huì)去執(zhí)行onDraw(),觸發(fā)onDraw一般是因?yàn)閘ayout過(guò)程中發(fā)現(xiàn)l,t,r,b和以前不一樣或者動(dòng)畫在執(zhí)行
-
requestLayout流程圖
requestLayout
當(dāng)調(diào)用xxxView#invalidate的時(shí)候會(huì)發(fā)生什么
//view#invalidate
public void invalidate() {
invalidate(true);
}
@UnsupportedAppUsage
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) {
return;
}
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
//...
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
//重點(diǎn)在這里
p.invalidateChild(this, damage);
}
//...
}
}
//viewgroup#invalidateChild
@Deprecated
@Override
public final void invalidateChild(View child, final Rect dirty) {
//...
ViewParent parent = this;
//...
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
//...重點(diǎn)這里
parent = parent.invalidateChildInParent(location, dirty);
//..
} while (parent != null);
}
}
- 調(diào)用了view.invalidate最終會(huì)循環(huán)調(diào)用parent.invalidateChildInParent。所以會(huì)找到頁(yè)面的爸爸ViewRootImpl執(zhí)行invalidateChildInParent和上面邏輯相似。至于viewgroup的invalidateChildInParent只是在設(shè)置臟區(qū)大小啥的
//ViewRootImpl#invalidateChildInParent
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
//會(huì)走scheduleTraversals
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
//...會(huì)走scheduleTraversals
invalidateRectOnScreen(dirty);
return null;
}
- scheduleTraversals后面也不用說(shuō)啥了又是doTraversal->performTraversals然后又是一通判斷是否要執(zhí)行measure、layout和draw
- invalidate流程圖

invalidate
- 到此就完結(jié)了
