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