前言

本文介绍了硬件加速和软件绘制的区别;
并梳理了最简单的情况,在子View(非ViewGroup)调用invalidate的流程,且忽略绘图缓存,即mLayerType == LAYER_TYPE_NONE
和requestLayout流程梳理。

硬件加速和软件绘制

  1. Software-based drawing model
    In the software drawing model, views are drawn with the following two steps:

    • Invalidate the hierarchy
    • Draw the hierarchy
      The Android system then draws any view in the hierarchy that intersects with the dirty region.
  2. Hardware accelerated drawing model
    Instead of executing the drawing commands immediately, the Android system records them inside display lists, which contain the output of the view hierarchy’s drawing code.
    Another optimization is that the Android system only needs to record and update display lists for views marked dirty by an invalidate.
    The new drawing model contains three stages:

    • Invalidate the hierarchy
    • Record and update display lists
    • Draw the display lists

硬件加速相比软件绘制的几个优点:

  1. 硬件加速收集绘制指令后,统一发送给RenderThread进行GPU绘制,分担UI线程压力
  2. 硬件加速只绘制标记为PFLAG_INVALIDATED的View,不会绘制与该View相交的区域

参考:
官方文档
理解Android硬件加速的小白文

invalidate软件绘制流程

从代码可知:

  1. View如果不可见&&无动画,ViewGroup不可见 && 无动画 && 无过渡动画不会执行invalidate
  2. 软件绘制会触发与dirty区域相交的所有View(硬件加速优化点)
  3. 根据第一点,ViewGroup#invalidate时会触发其包括自身和所有子View重绘
  4. 同一个draw时序内连续调用同一View的invalidate时,会被Flag阻挡,不再向下走
  5. 同一个draw时序内不同View调用invalidate时只会调为一个,不会重复执行

invalidate硬件加速流程

  1. View如果不可见&&无动画,ViewGroup不可见 && 无动画 && 无过渡动画不会执行invalidate
  2. 硬件绘制只会触发标记了PFLAG_INVALIDATED的View的draw()或dispatchDraw()
  3. View#invalidate时(非ViewGroup),因为被dispatchGetDisplayList接管了,不会调用dispatchDraw
  4. 同一个draw时序内连续调用同一View的invalidate时,会被Flag阻挡,不再向下走
  5. 同一个draw时序内不同View调用invalidate时只会调为一个,不会重复执行

requestLayout流程

  1. 清除measureCache,标记PFLAG_FORCE_LAYOUT,递归向上调用,整条路径都被标记,整条路径都会重薪measure、layout
  2. 可能会重新触发draw(),layout时大小变化后会触发invalidate
  3. 同一个layout时序内连续调用同一View的requestLayout时,会被isLayoutRequested阻挡,不再向上走
  4. 同一个layout时序内不同View调用requestLayout时只会调为一个,不会重复执行

相关代码如下:

View#invalidate

  void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {if (skipInvalidate()) {// View不可见 && 无动画,ViewGroup不可见 && 无动画 && 无过渡动画return;}/*
PFLAG_DRAWN:表示绘制好了,fullInvalidate后表示没绘制好,已经要重绘了,在child.draw()时置为1
PFLAG_HAS_BOUNDS: 已经layout完成,onLayout#setFrame中赋值
PFLAG_DRAWING_CACHE_VALID:buildDrawingCache()中赋值,表示此View对象的cache是否也需要被invalidate
PFLAG_INVALIDATED:是否需要重建View的display list*/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)) {if (fullInvalidate) {mLastIsOpaque = isOpaque();mPrivateFlags &= ~PFLAG_DRAWN; // 已经在流程中了}mPrivateFlags |= PFLAG_DIRTY;if (invalidateCache) {mPrivateFlags |= PFLAG_INVALIDATED; // 标记要被重绘,硬件加速以此为标识mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;}// 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); // 父控件重绘dirty区域}// Damage the entire projection receiver, if necessary.if (mBackground != null && mBackground.isProjected()) {final View receiver = getProjectionReceiver();if (receiver != null) {receiver.damageInParent();}}}
}

ViewGroup#invalidateChild

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;}do {parent = parent.invalidateChildInParent(location, dirty); // 最终到ViewRootImpl} while (parent != null);
}

ViewRootImpl

    // 硬件加速public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {mIsAnimating = true;}invalidate();}// 软件绘制public ViewParent invalidateChildInParent(int[] location, Rect dirty) {checkThread();if (dirty == null) {invalidate();return null;} else if (dirty.isEmpty() && !mIsAnimating) {// 不用重绘return null;}if (mCurScrollY != 0 || mTranslator != null) {mTempRect.set(dirty);dirty = mTempRect;if (mCurScrollY != 0) {dirty.offset(0, -mCurScrollY);}if (mTranslator != null) {mTranslator.translateRectInAppWindowToScreen(dirty);}if (mAttachInfo.mScalingRequired) {dirty.inset(-1, -1);}}invalidateRectOnScreen(dirty);return null;}private void invalidateRectOnScreen(Rect dirty) {final Rect localDirty = mDirty;if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {mAttachInfo.mSetIgnoreDirtyState = true;mAttachInfo.mIgnoreDirtyState = true;}// Add the new dirty rect to the current onelocalDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); // 并集// Intersect with the bounds of the window to skip// updates that lie outside of the visible regionfinal float appScale = mAttachInfo.mApplicationScale;final boolean intersected = localDirty.intersect(0, 0,(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); // 是否相交if (!intersected) {localDirty.setEmpty();}// 正在绘制中 或者 不相交&&不在动画中 即重绘if (!mWillDrawSoon && (intersected || mIsAnimating)) {scheduleTraversals(); // 等待VSync信息触发performTraversals}}private boolean draw(boolean fullRedrawNeeded) {if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {// 硬件绘制mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback); // updateViewTreeDisplayList,定点更新标记的View} else {// 软件绘制drawSoftware();  // --> mView.draw(canvas); 执行绘制流程}}}

ThreadedRenderer

    void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks,FrameDrawingCallback frameDrawingCallback) {updateRootDisplayList(view, callbacks);}private void updateRootDisplayList(View view, DrawCallbacks callbacks) {updateViewTreeDisplayList(view);}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;view.updateDisplayListIfDirty(); // 开始重绘流程view.mRecreateDisplayList = false;}

View

    // 更新dirtyDisplayListpublic RenderNode updateDisplayListIfDirty() {final RenderNode renderNode = mRenderNode;if (!canHaveDisplayList()) { // can't populate RenderNode, don't tryreturn renderNode;}if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0|| !renderNode.isValid()|| (mRecreateDisplayList)) {// Don't need to recreate the display list, just need to tell our// children to restore/recreate theirsif (renderNode.isValid()&& !mRecreateDisplayList) {// 当前View没有被标记为PFLAG_INVALIDATED,直接dispatchmPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;mPrivateFlags &= ~PFLAG_DIRTY_MASK;dispatchGetDisplayList(); // 代替dispatchDrawreturn renderNode; // no work needed}// If we got here, we're recreating it. Mark it as such to ensure that// we copy in child display lists into ours in drawChild()mRecreateDisplayList = true;int width = mRight - mLeft;int height = mBottom - mTop;int layerType = getLayerType();final DisplayListCanvas canvas = renderNode.start(width, height);try {if (layerType == LAYER_TYPE_SOFTWARE) {// 开启了位图缓存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 backgroundsif ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {// 不需要绘制,直接dispatch下去dispatchDraw(canvas);drawAutofilledHighlight(canvas);if (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().draw(canvas);}} else {// 重绘draw(canvas);}}} finally {renderNode.end(canvas);setDisplayListProperties(renderNode);}} else {mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;mPrivateFlags &= ~PFLAG_DIRTY_MASK;}return renderNode;}

ViewGroup

ViewRootImpl分发draw,到view#draw走到viewGroup的dispatchDraw,继续分发。

  protected void dispatchDraw(Canvas canvas) {for (int i = 0; i < childrenCount; i++) {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);}}}protected boolean drawChild(Canvas canvas, View child, long drawingTime) {return child.draw(canvas, this, drawingTime);}boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {// Sets the flag as early as possible to allow draw() implementations// to call invalidate() successfully when doing animationsmPrivateFlags |= PFLAG_DRAWN; // draw时序结束,表示已经一次invalidate完成if (!concatMatrix && (parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS | ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN //  判断当前view的矩形是否与canvas相交,与脏视图相交&& canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) && (mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;return more;}int layerType = getLayerType(); if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {if (layerType != LAYER_TYPE_NONE) {// If not drawing with RenderNode, treat HW layers as SWlayerType = LAYER_TYPE_SOFTWARE;buildDrawingCache(true); // 会触发view.draw(canvas)}cache = getDrawingCache(true);}final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;// 一般情况下falseif (!drawingWithDrawingCache) {if (drawingWithRenderNode) {mPrivateFlags &= ~PFLAG_DIRTY_MASK;((DisplayListCanvas) canvas).drawRenderNode(renderNode); // 直接绘制子View,不调用draw(canvas)} else {// Fast path for layouts with no backgroundsif ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {mPrivateFlags &= ~PFLAG_DIRTY_MASK;dispatchDraw(canvas); // ViewGroup自己不需要绘制,直接dispatch下去} else {draw(canvas);  // view绘制自己}}} else if (cache != null) {// LAYER_TYPE_SOFTWARE走到这里}}public void buildDrawingCache(boolean autoScale) {// flag标记了,别的view在invalidate时,自己不会走进去了if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0|| (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) {try {buildDrawingCacheImpl(autoScale);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}}protected void dispatchGetDisplayList() {final int count = mChildrenCount;final View[] children = mChildren;for (int i = 0; i < count; i++) {final View child = children[i];if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {// 可见 || 有动画时recreateChildDisplayList(child);}}}private void recreateChildDisplayList(View child) {child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; // 标记了么,标记了即需要重绘child.mPrivateFlags &= ~PFLAG_INVALIDATED;child.updateDisplayListIfDirty(); // 重绘child.mRecreateDisplayList = false;}

参考:

  • 官方文档

Android基础 硬件加速和软件绘制 invalidate和RequestLayout流程相关推荐

  1. android开发 硬件加速,Android 开发之硬件加速

    (1)定义 所谓硬件加速,指的是把某些计算工作交给专门的硬件来做,而不是和普通的计算工作一样交给 CPU 来处理.这样不仅减轻了 CPU 的压力,而且由于有了专门硬件的处理,这份计算工作的速度也被加快 ...

  2. android 加速 图像处理,Android之硬件加速

    所谓硬件加速,指的是把某些计算工作交给专门的硬件来做,而不是和普通的计算工作一样交给CPU 来处理.这样不仅减轻了 CPU 的压力,而且由于有了「专人」的处理,这份计算工作的速度也被加快了.这就是「硬 ...

  3. Android HWUI硬件加速模块浅析

    原址 什么是硬件加速(What) 传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行.Android系统负责硬件加速的模块主要是HWUI,如下图所示: 为什么要硬件加速(W ...

  4. ART简单体验和Android图形硬件加速分析

    原文链接:http://www.soomal.com/doc/10100004760.htm 平均/总评分:08.02/505 相关资料 收藏此图 相关图片 Android ART Android A ...

  5. 关于android 手机硬件加速问题

    关于安卓手机的硬件加速问题 分类: Android基础  2012-04-12 14:44  740人阅读  评论(0)  收藏  举报 最近听同事讨论硬件加速问题,完全不懂.于是乎,百度了下,原来从 ...

  6. android开发 硬件加速,Android开发的硬件加速

    说到硬件加快就必需和软件加快一路注释. (1)界说 所谓硬件加快,指的是把某些较量工作交给专门的硬件来做,而不是和通俗的较量工作一般交给 CPU 来处理.如许不光减轻了 CPU 的压力,并且因为有了专 ...

  7. Android 模拟器硬件加速

    转载自: https://software.intel.com/en-us/blogs/2012/03/12/how-to-start-intel-hardware-assisted-virtuali ...

  8. android view硬件加速,Android TextureView和硬件加速

    我正在尝试实现 this page上显示的示例.我已经尝试了运行android 4及更高版本的三个不同设备,并且在所有情况下,我都会看到带有此警告的黑屏: 01-27 20:01:22.683: W/ ...

  9. Android 关闭硬件加速时,导致一些问题

    当我们在中配置AndroidManifest中配置硬件加速 android:hardwareAccelerated="false",此时硬件加速已关闭,在我的开发过程中,我发现关闭 ...

最新文章

  1. AIX下删除LV后的现场保护和数据恢复方案
  2. 计算机网络-基本概念(3)【网络层】-路由选择协议
  3. 连载:阿里巴巴大数据实践—数据服务
  4. 安卓进阶系列-06数据库框架(LitePal)的使用
  5. 宝塔清mysql主从日志_宝塔面板Mysql主从日志文件mysql-bin文件清除方法
  6. JAVA入门级教学之(方法-1)
  7. 内存条能4+8混插吗?_笔记本内存条双通道提升有多大?实测FORESEE,你知道好处在哪吗...
  8. 反射创建对象_Go 反射:根据类型创建对象-第一部分(原始类型)
  9. php 读取数据库信息,php读取数据库信息的几种方法
  10. C#打开php链接传参然后接收返回值
  11. web_submit_data详解
  12. linux文件属性详解及文件类型
  13. 中文字体的英文名称 (simhei, simsun) 宋体 微软雅黑
  14. python堆叠柱状图加数字_Python 堆叠柱状图绘制方法
  15. BIOS锁定纯UEFI启动的解锁办法
  16. 美女SEO系列六:什么是友情链接?
  17. 微信公众平台三方授权登录(Java实现)
  18. 2021年转行软件测试能有前景吗?
  19. AttributeError: Got AttributeError when attempting to get a value for field
  20. 【055】工信部领导莅临翼辉参观指导工作

热门文章

  1. 错误1920(fontcache未能启动服务)后手动启动遇到错误1079的解决方法
  2. RAC11g 新增另一个网卡,DG同步
  3. 录音软件有哪些?这些软件值得收藏
  4. 书论41 苏轼《论书》
  5. 推荐收藏|又带来了12个炫酷的数据可视化作品含源码
  6. C++ Qt5学习笔记 2020-12-10(qlabel对齐方式,dateEdit显示日历,设置滚动条及其一些属性,qss中使用参数,QToolBox的使用,及以此实现简易的QQ好友列表)
  7. 数据库服务概述 、 构建MySQL服务 、 数据库基本管理 、 MySQL数据类型
  8. Sqlserver With用法
  9. python爬虫——爬取英雄联盟英雄基本信息
  10. HDR projects 7—HDR图片渲染工具