4.2.Android 硬件加速补充

具体源码分析在之后上传,因为涉及的东西特别多

参考

https://hencoder.com/ui-1-8/

https://blog.csdn.net/OneDeveloper/article/details/79791302

https://www.jianshu.com/p/f1feafffc365

概念

在正式开始之前需要说明一下,作为绘制部分的最后一期,本期内容只是为了内容的完整性做一个补充,因为之前好几期的内容里都有涉及硬件加速的技术点,而一些读者因为不了解硬件加速而产生了一些疑问。所以仅仅从难度上来讲,这期的内容并不难,并且本期的大部分内容你都可以从这两个页面中找到:

https://developer.android.google.cn/guide/topics/graphics/hardware-accel.html

https://www.youtube.com/watch?v=v9S5EO7CLjo

下面进入正题。

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

而对于 Android 来说,硬件加速有它专属的意思:在 Android 里,硬件加速专指把 View 中绘制的计算工作交给 GPU 来处理。

进一步地再明确一下,这个「绘制的计算工作」指的就是把绘制方法中的那些 Canvas.drawXXX() 变成实际的像素这件事。

原理

在硬件加速关闭的时候,Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,然后在之后的渲染过程中,这个 Bitmap 的像素内容被直接用于渲染到屏幕。这种绘制方式的主要计算工作在于把绘制操作转换为像素的过程(例如由一句 Canvas.drawCircle() 来获得一个具体的圆的像素信息),这个过程的计算是由 CPU 来完成的。大致就像这样:

而在硬件加速开启时,Canvas 的工作方式改变了:它只是把绘制的内容转换为 GPU 的操作保存了下来,然后就把它交给 GPU,最终由 GPU 来完成实际的显示工作。大致是这样:

如图,在硬件加速开启时,CPU 做的事只是把绘制工作转换成 GPU 的操作,这个工作量相对来说是非常小的。

怎么就「加速」了?

从上面的图中可以看出,硬件加速开启后,绘制的计算工作由 CPU 转交给了 GPU。不过这怎么就能起到「加速」作用,让绘制变快了呢?

硬件加速能够让绘制变快,主要有三个原因:

  1. 本来由 CPU 自己来做的事,分摊给了 GPU 一部分,自然可以提高效率;
  2. 相对于 CPU 来说,GPU 自身的设计本来就对于很多常见类型内容的计算(例如简单的圆形、简单的方形)具有优势;
  3. 由于绘制流程的不同,硬件加速在界面内容发生重绘的时候绘制流程可以得到优化,避免了一些重复操作,从而大幅提升绘制效率。

其中前两点可以总结为一句:用了 GPU,绘制就是快。原因很直观,不再多说。

关于第三点,它的原理我大致说一下:

  • 在硬件加速关闭时,绘制内容会被 CPU 转换成实际的像素,然后直接渲染到屏幕。具体来说,这个「实际的像素」,它是由 Bitmap来承载的(在硬件加速关闭的时候,Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,然后在之后的渲染过程中,这个 Bitmap 的像素内容被直接用于渲染到屏幕)。在界面中的某个 View 由于内容发生改变而调用 invalidate() 方法时,如果没有开启硬件加速,那么为了正确计算 Bitmap 的像素,这个View的父 View、父View 的父View 乃至一直向上直到最顶级 View,以及所有和它相交的兄弟 View,都需要被调用 invalidate()来重绘。一个 View 的改变使得大半个界面甚至整个界面都重绘一遍,这个工作量是非常大的。
  • 而在硬件加速开启时,绘制的内容会被转换成 GPU 的操作保存下来(承载的形式称为 display list,对应的类也叫做 DisplayList),再转交给 GPU。由于所有的绘制内容都没有变成最终的像素,所以它们之间是相互独立的,那么在界面内容发生改变的时候,只要把发生了改变的 View 调用 invalidate() 方法以更新它所对应的 GPU 操作就好,至于它的父 View 和兄弟 View,只需要保持原样。那么这个工作量就很小了。

正是由于上面的原因,硬件加速不仅是由于 GPU 的引入而提高了绘制效率,还由于绘制机制的改变,而极大地提高了界面内容改变时的刷新效率。

这里有个点需要注意,不管有没有硬件加速,在屏幕上绘制 UI 的时候,还是需要重新绘制整个屏幕的内容,而不是单单绘制发生变化的内容部分,之所以加速,是因为有了 Display List 把相关的操作缓存下来,而不用每次再去重复操作了。

所以把上面的三条压缩总结一下,硬件加速更快的原因有两条:

  • 用了 GPU,绘制变快了;
  • 绘制机制的改变,导致界面内容改变时的刷新效率极大提高。

layer type

在之前几期的内容里我提到过几次,如果你的绘制操作不支持硬件加速,你需要手动关闭硬件加速来绘制界面,关闭的方式是通过这行代码:

view.setLayerType(LAYER_TYPE_SOFTWARE, null);

硬件加速可以使用 setLayerType() 来关闭硬件加速,但这个方法其实是用来设置 View Layer 的:

  1. 参数为 LAYER_TYPE_SOFTWARE 时,使用软件来绘制 View Layer,绘制到一个 Bitmap,并顺便关闭硬件加速;
  2. 参数为 LAYER_TYPE_HARDWARE 时,使用 GPU 来绘制 View Layer,绘制到一个 OpenGL texture(如果硬件加速关闭,那么行为和 VIEW_TYPE_SOFTWARE 一致);
  3. 参数为 LAYER_TYPE_NONE 时,关闭 View Layer。

View Layer 可以加速无 invalidate() 时的刷新效率,但对于需要调用 invalidate() 的刷新无法加速。

View Layer 绘制所消耗的实际时间是比不使用 View Layer 时要高的,所以要慎重使用。

有不少人都有过疑问:什么是 layer type?如果这个方法是硬件加速的开关,那么它的参数为什么不是一个 LAYER_TYPE_SOFTWARE 来关闭硬件加速以及一个 LAYER_TYPE_HARDWARE 来打开硬件加速这么两个参数,而是三个参数,在 SOFTWARE 和 HARDWARE 之外还有一个 LAYER_TYPE_NONE?难道还能既不用软件绘制,也不用硬件绘制吗?

事实上,这个方法的本来作用并不是用来开关硬件加速的,只是当它的参数为 LAYER_TYPE_SOFTWARE 的时候,可以「顺便」把硬件加速关掉而已;并且除了这个方法之外,Android 并没有提供专门的 View 级别的硬件加速开关,所以它就「顺便」成了一个开关硬件加速的方法。

View Layer 是 View Layer,硬件加速是硬件加速,两者是两个概念,但是两者存在着交叉的地方。

所谓 View Layer,又称为离屏缓冲(Off-screen Buffer),它的作用是单独启用一块地方来绘制这个 View ,而不是使用软件绘制的 Bitmap 或者通过硬件加速的 GPU。这块「地方」可能是一块单独的 Bitmap,也可能是一块 OpenGL 的纹理(texture,OpenGL 的纹理可以简单理解为图像的意思),具体取决于硬件加速是否开启。

使用 setLayerType() 方法有什么好处呢,那就是在设置了 View Layer 之后,可以在原基础上(如果没有开启硬件加速,则在没有开启的基础上;如果开启了硬件加速,则在开启了的基础上),更进一步的将 View 的重绘效率提高。

采用什么来绘制 View 不是关键,关键在于当设置了 View Layer 的时候(非 NONE ),它的绘制会被缓存下来,就是使用了 Off-screen buffers,而且缓存的是最终的绘制结果,不是像硬件加速那样只是把 GPU 的操作保存下来再交给 GPU 去计算,因为在硬件加速开启时,需要绘制的内容会被转换成 GPU 的操作保存下来,承载的形式称为 display list,对应的类也叫做 DisplayList,再转交给 GPU,但是所有的绘制内容都没有变成最终的像素,因此每次绘制的时候,还是需要将 DisplayList 中保存的内容转换为最终的像素。

通过这样更进一步的缓存方式,View 的重绘效率进一步提高了:只要绘制的内容没有变,那么不论是 CPU 绘制(基于软件绘制模型)还是 GPU 绘制(硬件加速模型),它们都不用重新计算,而只要使用用之前缓存的绘制结果就可以了。

基于这样的原理,在进行移动、旋转等(无需调用 invalidate())的属性动画的时候开启 Hardware Layer 将会极大地提升动画的效率,因为在动画过程中 View 本身并没有发生改变,只是它的位置或角度改变了,而这种改变是可以由 GPU 通过简单计算就完成的,并不需要重绘整个 View。所以在这种动画的过程中开启 Hardware Layer,可以让本来就依靠硬件加速而变流畅了的动画变得更加流畅。实现方式大概是这样:

view.setLayerType(LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);

animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(LAYER_TYPE_NONE, null);
    }
});

animator.start();  

或者如果是使用 ViewPropertyAnimator,那么更简单:

view.animate()
        .rotationY(90)
        .withLayer(); // withLayer() 可以自动完成上面这段代码的复杂操作

不过,一定要注意,只有你在对 translationX translationY rotation alpha 等无需调用 invalidate() 的属性做动画的时候,这种方法才适用,因为这种方法本身利用的就是当界面不发生时,缓存未更新所带来的时间的节省。

所以简单地说——这种方式不适用于基于自定义属性绘制的动画。一定记得这句话。

另外,由于设置了 View Layer 后,View 在初次绘制时以及每次 invalidate() 后重绘时,需要进行两次的绘制工作(一次绘制到 Layer,一次从 Layer 绘制到显示屏),所以其实它的每次绘制的效率是被降低了的。所以一定要慎重使用 View Layer,在需要用到它的时候再去使用。

另外,除了用于关闭硬件加速和辅助属性动画这两项功能外,Layer 还可以用于给 View 增加一些绘制效果,例如设置一个 ColorMatrixColorFilter 来让 View 变成黑白的:

ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));

view.setLayerType(LAYER_TYPE_HARDWARE, paint);  

window硬件加速的开启和layer type的关系

  • ??canvas.savelayer不管是否开启了硬件加速,layer type设置如何,都可以创建一个bitmap,即离屏缓存。
  • 如果window硬件加速没有开启,

LAYER_TYPE_NONE,就表示不会创建drawing cache,不管是否invalidate,都会把view的绘制代码从头到尾执行一遍。

LAYER_TYPE_HARDWARE,和 LAYER_TYPE_SOFTWARE一样。

LAYER_TYPE_SOFTWARE,会创建drawing cache(buildDrawingCache),把绘制操作绘制到bitmap,

  • 如果window硬件加速开启

LAYER_TYPE_NONE,就表示不会创建hardware layer,就是硬件加速的绘制流程

LAYER_TYPE_HARDWARE,会绘制到一个hardware layer上,类似于bitmap,所以只要 除了那些不会调用invalidate的属性,其他属性修改后就会重绘。

LAYER_TYPE_SOFTWARE,会创建drawing cache(buildDrawingCache),把绘制操作绘制到bitmap

public void setLayerType(int layerType, @Nullable Paint paint) {
    if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
        throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
                + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
    }

    //告诉底层是否开启硬件层,如果开启,那么之后的绘制都是在这个硬件层上进行的。
    boolean typeChanged = mRenderNode.setLayerType(layerType);

    if (!typeChanged) {
        setLayerPaint(paint);
        return;
    }

    if (layerType != LAYER_TYPE_SOFTWARE) {
        // Destroy any previous software drawing cache if present
        // NOTE: even if previous layer type is HW, we do this to ensure we‘ve cleaned up
        // drawing cache created in View#draw when drawing to a SW canvas.
        destroyDrawingCache();
    }

    mLayerType = layerType;
    mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
    mRenderNode.setLayerPaint(mLayerPaint);

    // draw() behaves differently if we are on a layer, so we need to
    // invalidate() here
    invalidateParentCaches();
    invalidate(true);
}

使用注意

对所有的缓存来讲,都有一个缓存失效的可能性。任何时候如果在动画过程中调用view.invalidate(),那么layer就必须要重新渲染。经常的废弃hardware layers会比没有layers的情况下更糟糕,因为如同上面讲到的hardware layers在设置缓存时会有额外的开销。如果你需要经常的重新缓存layer,那就会有极大的损害。

这个问题也是非常容易出现的,因为动画经常有多个移动的部分。假如现在有一个三个部分移动的动画:

Parent ViewGroup

—-> Child View1 (往左移动)

—-> Child View2 (往右移动)

—-> Child View3 (往上移动)

如果你只在父布局ViewGroup上设置一个layer,那就将经常的缓存失效,因为ViewGroup会随着子View不断地改变。然而对每个单独的子Views而言,他们只是在位移。这种情况下,最好是对每个子View上设置Hardware Layer(而不是在父布局上)。

再次重申,通常是对多个子View上适当的设置Hardware Layer,这样他们就不会在动画运行时失效。

几种layer type的性能表现

  1. 如果只是单纯的做动画,不动态修改 View 的内容,那么性能表现为 :Hardware Layer >= Software Layer > Normal Layer
  2. 如果做动画同时动态修改 View 的内容,那么性能表现为 :Normal Layer > Software Layer = Hardware Layer
  3. Hardware Layer 对动画性能确实有很大的提升,但是如果你用不好,那么还不如不用
  4. 如果通过 Systrace 发现你做动画的时候每一帧都在 buildDrawingCache/SW(主线程) 或者 buildLayer(渲染线程),那么请查看你的代码的逻辑
  5. 有些情况下是由于系统的原因,比如图片比 Cache 大,invalidate 逻辑问题,可以联系手机厂商进行一起修改

问题

LAYER_TYPE_SOFTWARE时,如果调用了setAlpah/setTranslationX等方法会使用drawing cache,还是会重绘?

虽然进行了从上到下的遍历,但并没有进行任何的重绘或重建displaylist。

硬件加速开启后,调用了setAlpah/setTranslationX等方法会让父view进行重绘吗?

不会

原文地址:https://www.cnblogs.com/muouren/p/11706408.html

时间: 2024-10-01 09:59:01

4.2.Android 硬件加速补充的相关文章

Android——硬件加速(Hardware Acceleration(硬件加速)

从Android3.0(API Level 11)开始,Android 2D渲染管道能够更好的支持硬件加速.硬件加速执行的所有的绘图操作都是使用GPU在View对象的画布上来进行的.因为启用硬件加速会增加资源的需求,因此这样的应用会占用更多的内存. 硬件加速在target api大于等于14的情况下,是默认开启的,但是我们也可以显示的开启硬件加速.如果应用程序只使用标准的View和Drawable,那么打开全局硬件加速不会导致任何不良的绘制影响.然而,由于硬件加速并不支持所有的2D图形绘制操作,

Android硬件加速

概述 从Android 3.0开始(API L11),Android开始全面使用硬件加速来进行2D渲染,硬件加速是指Android中在View上进行绘制的图形图像都使用GPU来进行绘制,使用硬件加速,在大部分时候都让绘制更加流畅,但付出的代价是需要消耗更多的内存资源. 硬件加速在API L14之上是默认开启的,对于基本的View绘制,通过硬件加速可以增加绘图的流程性,但是要注意的是,并不是所有的2D图形绘制API都支持硬件加速. 通过开发者选项中的"强制进行GPU渲染",用户可以为全局

Android硬件加速介绍与实现

概述 在手机客户端尤其是Android应用的开发过程中,我们经常会接触到"硬件加速"这个词.由于操作系统对底层软硬件封装非常完善,上层软件开发者往往对硬件加速的底层原理了解很少,也不清楚了解底层原理的意义,因此常会有一些误解,如硬件加速是不是通过特殊算法实现页面渲染加速,或是通过硬件提高CPU/GPU运算速率实现渲染加速. 本文尝试从底层硬件原理,一直到上层代码实现,对硬件加速技术进行简单介绍,其中上层实现基于Android 6.0. 硬件加速对App开发的意义 对于App开发者,简单

Android 硬件加速

硬件加速可以提高渲染性能,但也会消耗更多内存.在 APK 版本14以后,硬件加速默认开启. 硬件加速并不支持所有2D绘图操作,详情可以查看官方文档. 个人经验中,由于应用里大量使用图片和 animation,以及频繁的创建和回收 bitmap,于是应用不频繁的出现过图片错乱的问题. W/Adreno-ES20﹕ <gl_draw_error_checks:580>: GL_INVALID_OPERATIOND/OpenGLRenderer﹕ GL error from OpenGLRender

Android HWUI硬件加速模块浅析

关键词:RenderNode,ThreadedRenderer,DisplayList,UvMapper,FontRenderer 原文:https://github.com/TsinStudio/AndroidDev 什么是硬件加速(What) 传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行.GPU相比CPU更加适合完成光栅化.动画变换等耗时任务,在移动设备上比起使用CPU来完成这些任务,GPU会更加省电些,带来的用户体验也会更佳. 为什么要硬件加速(Why)

4.1.Android的硬件加速官方文档

参考 https://developer.android.com/guide/topics/graphics/hardware-accel.html 硬件加速背景知识 在手机客户端尤其是Android应用的开发过程中,我们经常会接触到"硬件加速"这个词.由于操作系统对底层软硬件封装非常完善,上层软件开发者往往对硬件加速的底层原理了解很少,也不清楚了解底层原理的意义,因此常会有一些误解,如硬件加速是不是通过特殊算法实现页面渲染加速,或是通过硬件提高CPU/GPU运算速率实现渲染加速. 本

Android 硬件加速器及其问题小结 gif不显示

发现自己的手机上某个界面出现了花屏,某些控件背景被拉伸过多遮住了其他控件,很难看.这种现象高概率出现,分析了下发现:一旦发生这种现象,必然会打印下面这种log,google了下,这种log应该是硬件加速引起的.在从view层级关闭了硬件加速开关之后,问题没有再出现.[plain] view plaincopyprint? D/OpenGLRenderer(10887): GL error from OpenGLRenderer: 0x501      E/OpenGLRenderer(10887

Android 图形与硬件加速

今天在研究Android-PullToRefresh的时候,突然发现 清单文件中有这么一句 android:hardwareAccelerated="true".以前没有见过,果断百度,发现原来是与硬件加速有关,参见google开发者文档>   说是从Android3.0 开始 ,Android的2D 管道线就被设计成支持硬件加速的了.硬加速使用GPU承担了所有在View的canvas上执行的绘制操作.同时这样的应用也会消耗更多的内存.   启用硬加速最简单的的方法是对整个应用启

Android应用程序UI硬件加速渲染的预加载资源地图集服务(Asset Atlas Service)分析

我们知道,Android系统在启动的时候,会对一些系统资源进行预加载.这样不仅使得应用程序在需要时可以快速地访问这些资源,还使得这些资源能够在不同应用程序之间进行共享.在硬件加速渲染环境中,这些预加载资源还有进一步优化的空间.Android系统提供了一个地图集服务,负责将预加载资源合成为一个纹理上传到GPU去,并且能够在所有的应用程序之间进行共享.本文就详细分析这个预加载资源地图集服务的实现原理. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 资源预加载