android view绘制流程详解

news/2024/11/30 15:47:48/

 Android系统启动篇

1,《android系统启动流程简介》

2,《android init进程启动流程》

3,《android zygote进程启动流程》

4,《Android SystemServer进程启动流程》

5,《android launcher启动流程》

6,《Android Activity启动过程详解》

Android系统开发准备篇

1,《Android 源码下载和编译》

2,《android 11源码编译和pixel3 刷机》

3,《Android Framework代码IDE加载和调试》

Android系统开发实践篇

1,《android设置默认输入法》

2,《android framework预制APK应用》

3,《Android系统层面限制应用开机自启动详解》

4,《android单独编译framework模块并push》

5,《Android Framework开发系统问题分析》

Android系统开发核心知识储备篇

1,《Android编译系统-envsetup和lunch代码篇》

2,《Android编译系统-概念篇》

3,《android日志系统详解》

4,《Android系统Handler详解》

5,《Android系统Binder详解》

6,《Android中Activity、View和Window关系详解》

7,《android view绘制流程详解》

8,《Android读取系统属性详解》

9,《android 窗口管理机制详解》

10,《初识Android系统》

11,《android中AMS进程通知Zygote进程fork新进程的通信方式》

Android核心功能详解篇

1,《android应用市场点击下载APK安装详解》

2,《Android 手势导航(从下往上滑动进入多任务页面)》

3,《android手势分析(应用界面左往右边滑动退出应用)》

4,《android应用安装流程详解》

5,《android11安装应用触发桌面图标刷新流程》

6,《Android系统多任务Recents详解》

7,《android系统导航栏视图分析》

———————————————————————————————————————————

目录

一,背景介绍

二,核心概念

2.1 ViewRootImpl

2.2 requestLayout

2.3 invalidate

3 View的绘制

3.1 View绘制时序图

3.2 performTraversals

3.3 performDraw


一,背景介绍

        Android View的绘制流程分为三大流程:测量、布局、绘制。三大流程都开始于ViewRootImpl的performTraversals函数。

二,核心概念

2.1 ViewRootImpl

        ViewRootImpl 是一个视图层次结构的顶部,可以理解为一个 Window 中所有 View 的根 View 的管理者(但 ViewRootImpl 不是 View,只是实现了 ViewParent 接口),实现了 View 和 WindowManager 之间的通信协议,实现的具体细节在 WindowManagerGlobal 这个类中。

简单来说 ViewRootImpl 是 View 与 WindowManager 之间联系的桥梁,作用总结如下:

1.将 DecorView 传递给 WindowManagerSerive
2.完成 View 的绘制过程,包括 measure、layout、draw 过程
3.向 DecorView 分发收到的用户发起的 event 事件,如按键,触屏等事件。

其中,ViewRootImpl 中包含了两个需要重点关注的内部类:

    final class ViewRootHandler extends Handler 用于向 DecorView 分发事件
    static class W extends IWindow.Stub

W 是 ViewRootImp l的一个嵌入类,也是一个 Binder 服务。通过 mWindowSession.addToDisplay 函数传入 WMS,用来在 WMS 中通过 Binder 回调。

public void setView(View view, WindowManager.LayoutParams attrs,View panelParentView) {synchronized (this) {if (mView == null) {mView = view;......// Schedule the first layout -before- adding to the window// manager, to make sure we do the relayout before receiving// any other events from the system.requestLayout();//见下节介绍if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {mInputChannel = new InputChannel();//生成InputChannel}......try {......//调用mWindowSession.addToDisplay通过binder调用到WMS//实现对Window的真正的添加,这里的mWindow为 W 对象res = mWindowSession.addToDisplay(mWindow, mSeq, ......);setFrame(mTmpFrame);} catch (RemoteException e) {......} finally {......}......               if (mInputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}//用于输入事件的接收mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());}......}}}

2.2 requestLayout

路径frameworks/base/core/java/android/view/View.java

    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 hierarchyViewRootImpl viewRoot = getViewRootImpl();if (viewRoot != null && viewRoot.isInLayout()) {if (!viewRoot.requestLayoutDuringLayout(this)) {return;}}mAttachInfo.mViewRequestingLayout = this;}mPrivateFlags |= PFLAG_FORCE_LAYOUT;mPrivateFlags |= PFLAG_INVALIDATED;if (mParent != null && !mParent.isLayoutRequested()) {
//调用父布局mParent.requestLayout();}if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {mAttachInfo.mViewRequestingLayout = null;}}

其中,mParent.requestLayout(),mParent 的类型是 ViewParent,通过代码溯源 View#assignParent,

可以得出如下结论:
    DecorView (即根 View) 对应的 mParent 是 ViewRootImpl,普通子 View (非根 View) 对应的 mParent 是子 View 的父 View (即 ViewGroup)

2.3 invalidate

路径frameworks/base/core/java/android/view/View.java,

public void invalidate() {invalidate(true);
}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 (skipInvalidate()) {return;}......// Propagate the damage rectangle to the parent view.final AttachInfo ai = mAttachInfo;final ViewParent p = mParent;if (p != null && ai != null && l < r && t < b) {final Rect damage = ai.mTmpInvalRect;damage.set(l, t, r, b);p.invalidateChild(this, damage);}......
}

路径frameworks/base/core/java/android/view/ViewGroup.java,

@Override
public final void invalidateChild(View child, final Rect dirty) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null && attachInfo.mHardwareAccelerated) {// HW accelerated fast pathonDescendantInvalidated(child, child);return;}ViewParent parent = this;if (attachInfo != null) {final int[] location = attachInfo.mInvalidateChildLocation;location[CHILD_LEFT_INDEX] = child.mLeft;location[CHILD_TOP_INDEX] = child.mTop;......do {......parent = parent.invalidateChildInParent(location, dirty);......} while (parent != null);}
}

1.调用时机:
当前 View 树需要重绘时。如果当前 View 可见,则会调到 onDraw 方法。
该方法必须在 UI 线程调用。

2.View#invalidate 的调用逻辑:
从子 View -> 父 View -> DecorView -> ViewRootImpl 从子到父层层调用。

3 View的绘制

3.1 View绘制时序图

        如果对Activity的启动流程有一定了解的话,应该知道启动过程会在ActivityThread.java类中完成,在启动Activity的过程中,会调用到handleResumeActivity( )方法,关于视图的绘制过程最初就是从这个方法开始的。整个调用链如上图所示,直到ViewRootImpl类中的performTraversals()中,才正式开始绘制流程了,以该方法作为正式绘制的源头。

        在源码中,PhoneWindow和DecorView通过组合方式联系在一起的,而DecorView是整个View体系的根View。在前面handleResumeActivity()方法代码片段中,当Actiivity启动后,就通过addView方法,来间接调用ViewRootImpl类中的performTraversals(),从而实现视图的绘制。

3.2 performTraversals

        建立好了decorView与ViewRoot的关联后,ViewRoot类的requestLayout()方法会被调用,以完成应用程序用户界面的初次布局。实际被调用的是ViewRootImpl类的requestLayout()方法,这个方法的源码如下:

  @Overridepublic void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;scheduleTraversals();}}
        上面的方法中调用了scheduleTraversals()方法来调度一次完成的绘制流程,该方法会向主线程发送一个“遍历”消息,最终会导致ViewRootImpl的performTraversals()方法被调用。下面,我们以performTraversals()为起点,来分析View的整个绘制流程。
private void performTraversals() {......//如果当前View树中包含SurfaceView, 则执行surfaceCreated/surfaceChanged回调if (mSurfaceHolder != null) {if (mSurface.isValid()) {mSurfaceHolder.mSurface = mSurface;}mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);if (mSurface.isValid()) {if (!hadSurface) {mSurfaceHolder.ungetCallbacks();mIsCreating = true;SurfaceHolder.Callback callbacks[] =mSurfaceHolder.getCallbacks();if (callbacks != null) {for (SurfaceHolder.Callback c : callbacks) {c.surfaceCreated(mSurfaceHolder);}}surfaceChanged = true;}if (surfaceChanged || surfaceGenerationId !=mSurface.getGenerationId()) {SurfaceHolder.Callback callbacks[] =mSurfaceHolder.getCallbacks();if (callbacks != null) {for (SurfaceHolder.Callback c : callbacks) {c.surfaceChanged(mSurfaceHolder, lp.format,mWidth, mHeight);}}}mIsCreating = false;}} ......// mWidth&mHeight为Frame宽高, lp为setView传进来的// WindowManager.LayoutParams参数int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);......performLayout(lp, mWidth, mHeight);......// 调用OnGlobalLayoutListener#onGlobalLayoutif (triggerGlobalLayoutListener) {mAttachInfo.mRecomputeGlobalAttributes = false;mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();}......performDraw();......
}

主要做了一下工作:

    如果 View 树中当前 View 为 SurfaceView,则执行 surfaceCreated / surfaceChanged 相关回调
    依次执行 performMeasure -> performLayout -> performDraw
    OnGlobalLayoutListener#onGlobalLayout 回调中可以获取到 View 的真实宽高。因为该方法在 performMeasure -> performLayout 后面执行
 

3.3 performDraw

        这里是View真正绘制的入口,

private void performDraw() {......final Canvas canvas = mSurface.lockCanvas(dirty);canvas.setDensity(mDensity);canvas.translate(-xoff, -yoff);canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);......mView.draw(canvas);surface.unlockCanvasAndPost(canvas);
}

performDraw 调用流程:
draw(fullRedrawNeeded) -> drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets) -> mView.draw(canvas)
 

路径/frameworks/base/core/java/android/view/View.java,

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 neededint 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 contentonDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (isShowingLayoutBounds()) {debugDrawFocus(canvas);}// we're done...return;}/** Here we do the full fledged routine...* (this is an uncommon case where speed matters less,* this is why we repeat some of the tests that have been* done above)*/boolean drawTop = false;boolean drawBottom = false;boolean drawLeft = false;boolean drawRight = false;float topFadeStrength = 0.0f;float bottomFadeStrength = 0.0f;float leftFadeStrength = 0.0f;float rightFadeStrength = 0.0f;// Step 2, save the canvas' layersint paddingLeft = mPaddingLeft;final boolean offsetRequired = isPaddingOffsetRequired();if (offsetRequired) {paddingLeft += getLeftPaddingOffset();}int left = mScrollX + paddingLeft;int right = left + mRight - mLeft - mPaddingRight - paddingLeft;int top = mScrollY + getFadeTop(offsetRequired);int bottom = top + getFadeHeight(offsetRequired);if (offsetRequired) {right += getRightPaddingOffset();bottom += getBottomPaddingOffset();}final ScrollabilityCache scrollabilityCache = mScrollCache;final float fadeHeight = scrollabilityCache.fadingEdgeLength;int length = (int) fadeHeight;// clip the fade length if top and bottom fades overlap// overlapping fades produce odd-looking artifactsif (verticalEdges && (top + length > bottom - length)) {length = (bottom - top) / 2;}// also clip horizontal fades if necessaryif (horizontalEdges && (left + length > right - length)) {length = (right - left) / 2;}if (verticalEdges) {topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));drawTop = topFadeStrength * fadeHeight > 1.0f;bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));drawBottom = bottomFadeStrength * fadeHeight > 1.0f;}if (horizontalEdges) {leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));drawLeft = leftFadeStrength * fadeHeight > 1.0f;rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));drawRight = rightFadeStrength * fadeHeight > 1.0f;}saveCount = canvas.getSaveCount();int topSaveCount = -1;int bottomSaveCount = -1;int leftSaveCount = -1;int rightSaveCount = -1;int solidColor = getSolidColor();if (solidColor == 0) {if (drawTop) {topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);}if (drawBottom) {bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);}if (drawLeft) {leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);}if (drawRight) {rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);}} else {scrollabilityCache.setFadeColor(solidColor);}// Step 3, draw the contentonDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);// Step 5, draw the fade effect and restore layersfinal Paint p = scrollabilityCache.paint;final Matrix matrix = scrollabilityCache.matrix;final Shader fade = scrollabilityCache.shader;// must be restored in the reverse order that they were savedif (drawRight) {matrix.setScale(1, fadeHeight * rightFadeStrength);matrix.postRotate(90);matrix.postTranslate(right, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(rightSaveCount, p);} else {canvas.drawRect(right - length, top, right, bottom, p);}}if (drawLeft) {matrix.setScale(1, fadeHeight * leftFadeStrength);matrix.postRotate(-90);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(leftSaveCount, p);} else {canvas.drawRect(left, top, left + length, bottom, p);}}if (drawBottom) {matrix.setScale(1, fadeHeight * bottomFadeStrength);matrix.postRotate(180);matrix.postTranslate(left, bottom);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(bottomSaveCount, p);} else {canvas.drawRect(left, bottom - length, right, bottom, p);}}if (drawTop) {matrix.setScale(1, fadeHeight * topFadeStrength);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(topSaveCount, p);} else {canvas.drawRect(left, top, right, top + length, p);}}canvas.restoreToCount(saveCount);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (isShowingLayoutBounds()) {debugDrawFocus(canvas);}}

draw绘制流程,代码注释里已经给出,翻译下,

1,绘制背景
2,save layer (当有水平或垂直fading edges时)
3,onDraw 绘制当前View的内容
4,绘制当前View的子View
5,绘制 fading edges 和 restore layers (当有水平或垂直 fading edges 时)
6,onDrawForeground 绘制前景或滚动条
7,drawDefaultFocusHighlight 绘制获取焦点的 View 的 Focus 高亮
 


http://www.ppmy.cn/news/423139.html

相关文章

工控计算机系统,工控电脑装什么系统好

原标题&#xff1a;工控电脑装什么系统好 购买工控机的时候&#xff0c;我们都知道配置选择、性能选择很重要&#xff0c;但是操作系统选择的重要性却很少有人了解&#xff0c;要知道选对了合适的工控电脑硬件操作系统可以充分发挥出硬件应有的性能和潜力。那么问题来了&#x…

方便好用计算机,电脑系统哪个最好用 电脑系统对比介绍【图文】

电脑在我们日常生活当中的使用范围越来越广泛&#xff0c;不管是家庭还是工作&#xff0c;我们都离不开电脑&#xff0c;电脑不仅是我们工作当中的好帮手&#xff0c;还是我们生活当中一个不可缺少的小伙伴&#xff0c;使用电脑的人都知道&#xff0c;电脑的运行环境是操作系统…

旧电脑装什么系统最快_老电脑装什么系统好(不同配置不同系统推荐)

在整个Windows系统的家族中,Windows7是一个非常成功的系统版本。无论是从系统的流畅性还是兼容性和稳定性来讲,都是非常好的。不过由于微软官方的政策调整以及其他的一些原因,微软已经停止了对Win7系统的支持与服务。 尽管如此,还是有许许多多的Win7用户不愿意升级到Window…

电脑重装什么系统好 如何选择合适的系统

电脑重装什么系统好&#xff1f;众所周知&#xff0c;微软在2014年已经不再对XP系统提供技术支持&#xff0c;所以一般不建议大家装XP系统&#xff0c;但假如你是一个有情怀的人&#xff0c;那么也不会有人阻止你重温经典的。win7系统成为近年电脑系统的主角&#xff0c;老一点…

电脑装什么系统最稳定?

作为一个专业的有12年开店经历的电脑店老板&#xff0c;我真心地告诉你&#xff0c;没有什么是最稳定的&#xff0c;只有相对稳定的。 现在的电脑就两种&#xff0c;一种是品牌电脑&#xff0c;一种是组装电脑。 1、品牌电脑只要用的是出厂自带的原装系统&#xff0c;都很稳定…

cmake 官方教程 step5 下载和测试

Step 5: Installing and Testing 注意版本要求3.15 Often, it is not enough to only build an executable, it should also be installable. 可下载的 With CMake, we can specify install rules using the install() command. Supporting local installations for your buil…

足不出户怎么在家赚钱,暑假在家别闲着,给自己赚点生活费吧

在当今快节奏的现代生活中&#xff0c;人们面临着越来越大的竞争压力。为了过上舒适的生活、提前退休、创业或增加收入&#xff0c;许多人都希望能够在家中赚钱。那么&#xff0c;在家里如何可以找到赚钱的项目呢&#xff1f;本文将为您详细介绍一些方法。 一、在家工作有很多好…

什么牌子的高端游戏本比较好 外星人申请出战!

最近在各大论坛都能看到一类问题&#xff0c;大意是问什么牌子的高端游戏本比较好&#xff0c;引起了不少玩家的“共鸣”。说起来&#xff0c;这个话题是比较有趣的&#xff0c;因为在评论区里你可以看到很多不同的“跳坑经历”&#xff0c;什么散热不好游戏降频&#xff0c;买…