写点什么

View 系列:硬件加速,安卓面试项目

用户头像
Android架构
关注
发布于: 23 小时前

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);// 关闭

确定是否经过硬件加速

  • 如果 Vie


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


w 已附加到硬件加速窗口,则 **View.isHardwareAccelerated() **会返回 true。


  • 如果 Canvas 经过硬件加速,则**Canvas.isHardwareAccelerated() **会返回 true

[不受支持的绘制操作](

)

绘制模型(原理)

  • 软件绘制:使用 CPU 绘制到 Bitmap,然后把 Bitmap 渲染到屏幕

  • 硬件绘制:CPU 把绘制内容转换成 GPU 操作,由 GPU 负责真正的绘制


在硬件加速关闭的时候:


Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,在之后的渲染过程中,这个 Bitmap 的像素内容被直接渲染到屏幕。 这种绘制方式的主要计算工作在于把绘制操作转换为像素的过程,这个过程的计算是由 **CPU **来完成的。大致就像这样:



在硬件加速开启时:


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



硬件加速更快的原因:


  • GPU 分摊了工作,并且 GPU 计算效率更快

  • 绘制机制的改变,导致界面内容改变时的刷新效率极大提高。 (invalidate()时,其父 View 到顶层 View 不再都刷新)


硬件加速缺点:


兼容性问题 由于使用 GPU(暂时)无法完成某些绘制,因此对于一些特定的 API,需要关闭硬件加速,转回到使用 CPU 进行绘制


  • 自定义绘制时,[参看 api 兼容性](


)


  • 所有的原生自带控件,都没有用到 API 版本不兼容的绘制操作,可以放心使用

[离屏缓冲](

)


从 Android 3.0(API 级别 11)开始,可以通过 View.setLayerType()方法更好地控制如何及何时使用层。视图可以使用以下三种层类型之一:


  • LAYER_TYPE_NONE:视图正常渲染,不受屏幕外缓冲区支持。这是默认行为。

  • LAYER_TYPE_HARDWARE:如果应用经过硬件加速,视图在硬件中渲染为硬件纹理。如果应用未经过硬件加速,此层类型的行为方式与 LAYER_TYPE_SOFTWARE 相同。

  • LAYER_TYPE_SOFTWARE:使用软件来渲染视图,绘制到 Bitmap。并顺便关闭硬件加速


所谓 View Layer,又称为离屏缓冲(Off-screen Buffer),它的作用是单独启用一块地方来绘制这个 View ,而不是使用软件绘制的 Bitmap 或者通过硬件加速的 GPU


这块「地方」可能是一块单独的 Bitmap,也可能是一块 OpenGL 的纹理(texture,OpenGL 的纹理可以简单理解为图像的意思),具体取决于硬件加速是否开启。采用什么来绘制 View 不是关键,关键在于当设置了 View Layer 的时候,它的绘制会被缓存下来,而且缓存的是最终的绘制结果,而不是像硬件加速那样只是把 GPU 的操作保存下来再交给 GPU 去计算。通过这样更进一步的缓存方式,View 的重绘效率进一步提高了:只要绘制的内容没有变,那么不论是 CPU 绘制还是 GPU 绘制,它们都不用重新计算,而只要只用之前缓存的绘制结果就可以了


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


注意:只有在对 translationX 、translationY、 rotation、 alpha 等无需调用 invalidate() 的属性做动画的时候,这种方法才适用。因为这种方法本身利用的就是当界面不发生时,缓存未更新所带来的时间的节省。所以简单地说 这种方式不适用于基于自定义属性绘制的动画


总结


  • setLayerType(LAYER_TYPE_SOFTWARE), “顺带”关闭硬件加速

  • 关键点:translationX、 translationY 、rotation 、alpha 等无需调用 invalidate() 的属性做动画时,才适用

  • 不适用于基于自定义属性绘制的动画

使用

view.setLayerType

// 开启离屏缓存(软件渲染位图,并关闭硬件加速)myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);// 开启离屏缓存(硬件渲染位图,此时不一定正在硬件加速奥)myView.setLayerType(View.LAYER_TYPE_HARDWARE, null);


//硬件层会占用内存,因此仅在动画播放期间启用,然后在动画结束后停用 ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);animator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {view.setLayerType(View.LAYER_TYPE_NONE, null);// 关闭缓存}});animator.start();


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


class RoundRectCoverView(context: Context, attrs: AttributeSet) : View(context, attrs) {private val paint = Paint(Paint.ANTI_ALIAS_FLAG)private val porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)


init {//开启 View 级别的离屏缓冲,并关闭硬件加速,使用软件绘制 setLayerType(LAYER_TYPE_SOFTWARE, null)}override fun onDraw(canvas: Canvas) {canvas.drawRoundRect(mPadding, mPadding, width - mPadding,height - mPadding, mRoundCorner, mRoundCorner, paint)paint.color = mCoverColorpaint.xfermode = porterDuffXfermodecanvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)paint.xfermode = null}

canvas.saveLayer

override fun onDraw(canvas: Canvas) {//Canvas 的离屏缓冲 val count = canvas.saveLayer(bounds, paint)canvas.withSave {canvas.drawColor(mCoverColor)canvas.clipPath(clipPath)canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC)}//把离屏缓冲的内容,绘制到 View 上去 canvas.restoreToCount(count)}


对比:


  • setLayerType: 把整个View都绘制在离屏缓冲中

  • saveLayer:把部分View内容绘制在离屏缓冲中(优先使用该方式)

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
View系列:硬件加速,安卓面试项目