[置顶] DecorView绘制流程源码分析
2018-01-05 17:19
531 查看
通过Activiyt布局加载流程源码分析(I)和Activiyt布局加载流程源码分析(II)两篇博文,我们知道,首先,Activity的布局内容被加载进入装饰器DecorView中,然后WindowManager将DecorView添加到PhoneWindow中,也即Window中,最后ViewRootImpl对DecorView进行绘制操作,将其内容显示到手机上。但前两篇博文中,对于DecorView的绘制原理,没有作详细说明,所以本篇博文重在梳理这部分逻辑。
在开始分析之前,我们需要了解一些概念,如:
DecorView:是PhoneWindow中的一个内部类,也是Window的顶级View,主要负责装载各种View和Activity布局。
ViewRootImpl:是View的绘制的辅助类,所有View的绘制都离不开ViewRootImpl。
Choreographer:是”舞蹈指挥”者,控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。
DisplayEventReceiver:是一个抽象类,主要是接收显示绘制帧的垂直脉冲vsync,从而开始绘制帧。
FrameDisplayEventReceiver: Choreographer的内部类,也是DisplayEventReceiver具体实现类。
在说DecorView的绘制之前,我们先来说说Android的绘制原理,这样方便我们理解后面内容。
首先,我们来关注一下注释1,这里主要是对mView进行赋值DecorView,mView是ViewRootImpl的属性变量,这里需要注意一下,因为后面绘制需要用到。我们再来看注释2,ViewRootImpl的requestLayout()方法,我们具体来看看其方法逻辑
这里我们直接来看核心方法scheduleTraversals()
这里我们需要特别关注mChoreographer,即Choreographer类,从字面意思来说是“舞蹈指挥”者,是Android绘制原理的核心类,控制Android显示帧的绘制。关于Choreographer类,这里不做过多的分析,想了解其原理的同学,可以看看博文Android Choreographer 源码分析。
由Android绘制原理,我们知道每隔16ms,Android系统就会发出垂直信号VSYNC脉冲重绘我们的界面,而Choreographer中postCallback()方法主要功能就是向系统添加回调并加入绘制帧,从而实现View的绘制。这里我们来看看添加的回调mTraversalRunnable
我们继续来看doTraversal()方法
我们继续分析方法performTraversals()
此方法,可以说是Android系统绘制的核心方法。View绘制原理的三大流程:View的测量onMeasure -> View的布局onLayout -> View的绘制onDraw,都在此方法中提现出来了。下面我们一一来看一下相关方法,首先我们来看一下performMeasure()方法
根据上面的分析,我们知道mView就是DecorView,所以这里就是调用DecorView的measure()方法。由Activity布局加载流程源码分析(I)博文,我们知道DecorView是继承至FrameLayout,而FrameLayout又继承至ViewGroup,ViewGroup又继承至View,所以这里的measure()方法就是调用View中的measure()方法,具体怎么调用,这里不细说了,想了解的同学可以看看这篇博文View的绘制原理。下面让我们来看看performLayout()方法
这里逻辑与测量measure类似,也就是调用DecorView的layout方法,具体View的布局控制细节略。我们再来看看performDraw()方法
我们继续来看看draw()方法
这里的绘制方法涉及到两种绘制方式,分别为Hardware渲染(硬件加速)和Software渲染,关于选择那种绘制方式,这里还需要回溯到ViewRootImpl的setView()方法,我们再来看看此方法
我们知道DecorView是实现了RootViewSurfaceTaker接口的,所以当View为DecorView
c7e2
时,就不会开启硬件加速,不会走Hardware渲染,而其他的View会选择Hardware渲染。因为WindowManager添加的View可能使DecorView,也可能不是DecorView,也可能是一般的View。我们来看看enableHardwareAcceleration()方法
这方法主要是对mAttachInfo.mHardwareRenderer进行赋值,从而在performDraw()方法中可以执行绘制。下面我们来看看上面的绘制方式1,Hardware渲染(硬件加速),由上知主要是通过attachInfo.mHardwareRenderer.draw()绘制,所以我们来看看HardwareRenderer中的draw()方法
当displayList为空的时候,也就会调用 view.draw(canvas)方法,即DeocorView的draw()方法。关于DisplayList这里也不细说,它主要是View中的显示列表记录,具体作用这里不作详述了。我们再来看看第二种绘制方式SoftWare渲染,drawSoftware()方法
这里发现,最后也还是调用DecorView的draw方法,具体流程也与measure和layout类似。可以说两种绘制方式最后也还是调用了View的draw方法,可以说是殊途同归。
到这里我们就把添加的回调绘制帧mTraversalRunnable这个说完了。上面说到,Choreographer通过postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)方法向系统添加回调并加入绘制帧,然后Android系统通过16ms间隔脉冲实现帧的绘制,从而才将布局内容显示到手机上。
说到这里,DecorView的绘制流程我们就说完了。
注:源码采用android-4.1.1_r1版本,建议下载源码然后自己走一遍流程,这样更能加深理解。
Android App卡顿分析,以及使用Choreographer进行帧率统计监测
View的绘制原理
在开始分析之前,我们需要了解一些概念,如:
DecorView:是PhoneWindow中的一个内部类,也是Window的顶级View,主要负责装载各种View和Activity布局。
ViewRootImpl:是View的绘制的辅助类,所有View的绘制都离不开ViewRootImpl。
Choreographer:是”舞蹈指挥”者,控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。
DisplayEventReceiver:是一个抽象类,主要是接收显示绘制帧的垂直脉冲vsync,从而开始绘制帧。
FrameDisplayEventReceiver: Choreographer的内部类,也是DisplayEventReceiver具体实现类。
在说DecorView的绘制之前,我们先来说说Android的绘制原理,这样方便我们理解后面内容。
一、Android的绘制原理简介
Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity)。为什么是16ms, 因为Android设定的刷新率是60FPS(Frame Per Second), 也就是每秒60帧的刷新率, 约合16ms刷新一次。如下图所示:二、DecorView绘制原理分析
在Activity布局加载流程分析中,我们知道DecorView被添加进入了WindowManager,并且最后ViewRootImpl通过setView()方法开始绘制DecorView,所以下面我们就来看看ViewRootImpl的setView()方法public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view;//1.Dec 4000 orView赋值为mView mFallbackEventHandler.setView(view); ...... requestLayout();//2.DecorView的绘制 if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); //3.Window的权限判断,主要是Window添加的控制,由于本篇博文重在DecorView绘制,所以这里将不会分析 res = sWindowSession.add(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } ........ } } }
首先,我们来关注一下注释1,这里主要是对mView进行赋值DecorView,mView是ViewRootImpl的属性变量,这里需要注意一下,因为后面绘制需要用到。我们再来看注释2,ViewRootImpl的requestLayout()方法,我们具体来看看其方法逻辑
public void requestLayout() { checkThread(); mLayoutRequested = true; scheduleTraversals();//核心方法 }
这里我们直接来看核心方法scheduleTraversals()
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); scheduleConsumeBatchedInput(); } }
这里我们需要特别关注mChoreographer,即Choreographer类,从字面意思来说是“舞蹈指挥”者,是Android绘制原理的核心类,控制Android显示帧的绘制。关于Choreographer类,这里不做过多的分析,想了解其原理的同学,可以看看博文Android Choreographer 源码分析。
由Android绘制原理,我们知道每隔16ms,Android系统就会发出垂直信号VSYNC脉冲重绘我们的界面,而Choreographer中postCallback()方法主要功能就是向系统添加回调并加入绘制帧,从而实现View的绘制。这里我们来看看添加的回调mTraversalRunnable
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal();//核心方法 } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
我们继续来看doTraversal()方法
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); try { performTraversals();//核心方法 } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
我们继续分析方法performTraversals()
private void performTraversals() { .......//1.代码省略。省略主要内容,Surface和SurfaceHolder初始化及条件判断 if (!mStopped) { boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//2.执行View的宽高测量 ........ layoutRequested = true; } } } final boolean didLayout = layoutRequested && !mStopped; boolean triggerGlobalLayoutListener = didLayout || attachInfo.mRecomputeGlobalAttributes; if (didLayout) { performLayout();//3.执行View的布局 ..... } ...... boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() || viewVisibility != View.VISIBLE; if (!cancelDraw && !newSurface) { if (!skipDraw || mReportNextDraw) { ........ performDraw();//执行View绘制 } } else { ........ } }
此方法,可以说是Android系统绘制的核心方法。View绘制原理的三大流程:View的测量onMeasure -> View的布局onLayout -> View的绘制onDraw,都在此方法中提现出来了。下面我们一一来看一下相关方法,首先我们来看一下performMeasure()方法
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//核心方法 } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
根据上面的分析,我们知道mView就是DecorView,所以这里就是调用DecorView的measure()方法。由Activity布局加载流程源码分析(I)博文,我们知道DecorView是继承至FrameLayout,而FrameLayout又继承至ViewGroup,ViewGroup又继承至View,所以这里的measure()方法就是调用View中的measure()方法,具体怎么调用,这里不细说了,想了解的同学可以看看这篇博文View的绘制原理。下面让我们来看看performLayout()方法
private void performLayout() { mLayoutRequested = false; mScrollMayChange = true; final View host = mView; if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { Log.v(TAG, "Laying out " + host + " to (" + host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
这里逻辑与测量measure类似,也就是调用DecorView的layout方法,具体View的布局控制细节略。我们再来看看performDraw()方法
private void performDraw() { if (!mAttachInfo.mScreenOn && !mReportNextDraw) { return; } final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); try { draw(fullRedrawNeeded);//核心方法 } finally { mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } ...... }
我们继续来看看draw()方法
private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; ....... if (!dirty.isEmpty() || mIsAnimating) { if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) { // Draw with hardware renderer. mIsAnimating = false; mHardwareYOffset = yoff; mResizeAlpha = resizeAlpha; mCurrentDirty.set(dirty); mCurrentDirty.union(mPreviousDirty); mPreviousDirty.set(dirty); dirty.setEmpty(); //1.Hardware渲染(Hardware加速) if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,animating ? null :mCurrentDirty)) { mPreviousDirty.set(0, 0, mWidth, mHeight); } } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {//2.Software渲染 return; } } if (animating) { mFullRedrawNeeded = true; scheduleTraversals(); } }
这里的绘制方法涉及到两种绘制方式,分别为Hardware渲染(硬件加速)和Software渲染,关于选择那种绘制方式,这里还需要回溯到ViewRootImpl的setView()方法,我们再来看看此方法
/** * We have one child */ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ...... if (view instanceof RootViewSurfaceTaker) {//1.mSurfaceHolder赋值 mSurfaceHolderCallback = ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); } } ........ if (mSurfaceHolder == null) {//2.是否需要硬件加速 enableHardwareAcceleration(mView.getContext(), attrs); } ....... } }
我们知道DecorView是实现了RootViewSurfaceTaker接口的,所以当View为DecorView
c7e2
时,就不会开启硬件加速,不会走Hardware渲染,而其他的View会选择Hardware渲染。因为WindowManager添加的View可能使DecorView,也可能不是DecorView,也可能是一般的View。我们来看看enableHardwareAcceleration()方法
private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; if (mTranslator != null) return; final boolean hardwareAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; if (hardwareAccelerated) { if (!HardwareRenderer.isAvailable()) { return; } final boolean fakeHwAccelerated = (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0; final boolean forceHwAccelerated = (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0; if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) { ........ final boolean translucent = attrs.format != PixelFormat.OPAQUE; mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);//核心方法 mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = mAttachInfo.mHardwareRenderer != null; } else if (fakeHwAccelerated) { mAttachInfo.mHardwareAccelerationRequested = true; } } }
这方法主要是对mAttachInfo.mHardwareRenderer进行赋值,从而在performDraw()方法中可以执行绘制。下面我们来看看上面的绘制方式1,Hardware渲染(硬件加速),由上知主要是通过attachInfo.mHardwareRenderer.draw()绘制,所以我们来看看HardwareRenderer中的draw()方法
@Override boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { if (canDraw()) { ....... try { ....... DisplayList displayList;//渲染列表 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); try { displayList = view.getDisplayList(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } ....... if (displayList != null) { ..... try { status |= canvas.drawDisplayList(displayList, mRedrawClip, DisplayList.FLAG_CLIP_CHILDREN); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } ..... handleFunctorStatus(attachInfo, status); } else { view.draw(canvas);//核心方法 } } finally { .... } ...... return dirty == null; } } return false; }
当displayList为空的时候,也就会调用 view.draw(canvas)方法,即DeocorView的draw()方法。关于DisplayList这里也不细说,它主要是View中的显示列表记录,具体作用这里不作详述了。我们再来看看第二种绘制方式SoftWare渲染,drawSoftware()方法
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty) { // Draw with software renderer. Canvas canvas; try { ....... canvas = mSurface.lockCanvas(dirty); ...... } catch (Surface.OutOfResourcesException e) { ..... } catch (IllegalArgumentException e) { ..... } try { ....... try { ...... mView.draw(canvas);//核心方法 drawAccessibilityFocusedDrawableIfNeeded(canvas); } finally { if (!attachInfo.mSetIgnoreDirtyState) { // Only clear the flag if it was not set during the mView.draw() call attachInfo.mIgnoreDirtyState = false; } } } finally { ..... } return true; }
这里发现,最后也还是调用DecorView的draw方法,具体流程也与measure和layout类似。可以说两种绘制方式最后也还是调用了View的draw方法,可以说是殊途同归。
到这里我们就把添加的回调绘制帧mTraversalRunnable这个说完了。上面说到,Choreographer通过postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)方法向系统添加回调并加入绘制帧,然后Android系统通过16ms间隔脉冲实现帧的绘制,从而才将布局内容显示到手机上。
说到这里,DecorView的绘制流程我们就说完了。
注:源码采用android-4.1.1_r1版本,建议下载源码然后自己走一遍流程,这样更能加深理解。
三、参考文档
Android Choreographer 源码分析Android App卡顿分析,以及使用Choreographer进行帧率统计监测
View的绘制原理
相关文章推荐
- [置顶] View绘制三大流程源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android UI绘制流程源码分析
- Android应用层View绘制流程与源码分析
- 源码分析篇 - Android绘制流程(三)requestLayout()与invalidate()流程及Choroegrapher类分析
- Android应用层View绘制流程与源码分析
- 源码分析篇 - Android绘制流程(一)窗口启动流程分析
- Android应用层View绘制流程与源码分析
- (二)UI绘制流程-绘制过程源码分析
- Android笔记--View绘制流程源码分析(一)
- 源码分析Android中View的绘制流程
- Android应用层View绘制流程与源码分析,性能优化
- Android应用层View绘制流程与源码分析
- 源码分析-从ActivityThread到View绘制流程
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析