Android高级进阶之-性能优化-UI优化

为什么会有UI优化的概念?因为UI过程过于复杂,就会导致界面、动画卡顿,影响用户体验。

屏幕上的图形、文字等,都是经过CUP和GPU的计算,然后绘制到屏幕上的,所以首要任务就是了解CPU和GPU的工作流程。

CPU的任务繁重,除做逻辑运算外,还要做内存管理、显示操作,因此在实际运算的时候性能会大打折扣,在没有GPU的时代,不能显示负责的图形,其运算速度远跟不上今天复杂三维游戏的要求。这时就需要专门负责显示计算的GPU。

image.png
image.png

黄色的 Control 为控制器,用于协调控制整个 CPU 的运行,包括取出指令、控制其他模块的运行等; 绿色的 ALU ( Arithmetic Logic Unit )是算术逻辑单元,用于进行数学、逻辑运算;
橙色的 Cache 和 DRAM 分别为缓存和 RAM ,用于存储信息。

从结构图可以看出, CPU 的控制器较为复杂,而 ALU 数量较少。因此 CPU 擅长各种复杂 的逻辑运算,但不擅长数学尤其是浮点运算。

image.png

CPU负责将图形元素进行一系列初步的运算,得到图形的纹理信息,然后将纹理信息丢给GPU,GPU负责将纹理信息进行更复杂的运算,得到最终的位图,显示器直接按照位图的像素信息渲染,就完成了显示图形的整个流程。

60Hz 刷新频率由来

12 fps :由于人类眼睛的特殊生理结构,如果所看画面之帧率高于每秒约 10-12 帧的时候,就会 认为是连贯的
24 fps :有声电影的拍摄及播放帧率均为每秒 24 帧,对一般人而言已算可接受
30 fps :早期的高动态电子游戏,帧率少于每秒 30 帧的话就会显得不连贯,这是因为没有动态模 糊使流畅度降低
60 fps 在与手机交互过程中,如触摸和反馈 60 帧以下人是能感觉出来的。 60 帧以上不能察觉 变化
当帧率低于 60 fps 时感觉的画面的卡顿和迟滞现象

Android 系统每隔 16ms 发出 VSYNC 信号 (1000ms/60=16.66ms) ,触发对 UI 进行渲染, 如果每次渲染都成 功这样就能够达到流畅的画面所需要的 60fps ,为了能够实现 60fps ,这意味着计算渲染的大多数操作都必须 在 16ms 内完成。

image.png

16 毫秒的时间主要被两件事情所占用
第一件:将 UI 对象转换为一系列多边形和纹理。
第二件: CPU 传递处理数据到 GPU 。
所以很明显,我们要缩短 这两部分的时间,也就是说需要尽量减少对象转换的次数,以及GPU上传数据的次数。

如何减少这两部分的时间 以至于在 16ms 完成呢?
CPU 减少 xml 转换成对象的时间
GPU 减少重复绘制的时间

image.png

image.png

Android主题会有背景,我们可以考虑去除这个背景,减少一次不必要的渲染。


image.png

另外,我们的布局文件如果不是业务需要,尽量不指定背景。


image.png

自定义view,减少过度绘制
先看效果


优化之前
优化之后

很明显,中间区域在未优化之前,三中图片有重叠部分,明显存在过度绘制问题。
优化前的代码:

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        mDroidCards.forEachIndexed { index, droidCard ->
            if (index == mDroidCards.lastIndex) {
                drawDroidCard(canvas, droidCard)
            } else {
                drawDroidCard(canvas, droidCard, index)
            }
        }

        invalidate()
    }

    private fun drawDroidCard(canvas: Canvas?, droidCard: DroidCard, index: Int) {
//        canvas?.save()
//        canvas?.clipRect(droidCard.x.toFloat(), 0f, mDroidCards[index + 1].x.toFloat(), droidCard.height.toFloat())
        drawDroidCard(canvas, droidCard)
//        canvas?.restore()
    }

    private fun drawDroidCard(canvas: Canvas?, droidCard: DroidCard) {
        canvas?.drawBitmap(droidCard.bitmap, droidCard.x.toFloat(), 0f, mPaint)
    }

优化后的代码:

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        mDroidCards.forEachIndexed { index, droidCard ->
            if (index == mDroidCards.lastIndex) {
                drawDroidCard(canvas, droidCard)
            } else {
                drawDroidCard(canvas, droidCard, index)
            }
        }

        invalidate()
    }

    private fun drawDroidCard(canvas: Canvas?, droidCard: DroidCard, index: Int) {
        canvas?.save()
        canvas?.clipRect(droidCard.x.toFloat(), 0f, mDroidCards[index + 1].x.toFloat(), droidCard.height.toFloat())
        drawDroidCard(canvas, droidCard)
        canvas?.restore()
    }

    private fun drawDroidCard(canvas: Canvas?, droidCard: DroidCard) {
        canvas?.drawBitmap(droidCard.bitmap, droidCard.x.toFloat(), 0f, mPaint)
    }

结论:
通过canvas.clipRect方法,裁剪出需要绘制的区域,避免重复绘制。

使用Layout Inspector检查View hierarchy。


image.png

这里可以清晰看到View的树桩结构,我们需要减少树的层级,减少控件的渲染时间。

可以使用merge标签简化层级。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 界面是 Android 应用中直接影响用户体验最关键的部分。如果代码实现得不好,界面容易发生卡顿且导致应用占用...
    passiontim阅读 1,836评论 0 8
  • 界面是 Android 应用中直接影响用户体验最关键的部分。如果代码实现得不好,界面容易发生卡顿且导致应用占用大量...
    Ten_Minutes阅读 699评论 0 9
  • 注:本文是我在 Android 界面性能调优知识的系统性总结,纯属个人碎碎念。秉持开源分享的原则发布本文出来,各位...
    东经315度阅读 765评论 0 8
  • 昨夜碧天 倏生眩目的颜色 长睫挥动情云 遥遥迢迢 苦恋浮移的背影 以往河边 谁的琴如流水 淹没身后 几许 潇洒...
    龙生于海阅读 253评论 8 37
  • 偶然从朋友圈知道了这样一个APP,好奇与最初的写作之梦驱使我下载下来,许久都不曾读书写字,想要写上一大段话...
    S苏她阅读 261评论 0 0