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 hierarchy
            ViewRootImpl 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 path
        onDescendantInvalidated(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()方法,这个方法的源码如下:

  @Override
    public 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#onGlobalLayout
    if (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 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;
        }

        /*
         * 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' layers
        int 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 artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (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 content
        onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        // must be restored in the reverse order that they were saved
        if (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 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);
        }
    }

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 高亮
 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐