• 深入Android渲染机制


    1.知识储备

    • CPU: 中央处理器,它集成了运算,缓冲,控制等单元,包括绘图功能.CPU将对象处理为多维图形,纹理(Bitmaps、Drawables等都是一起打包到统一的纹理).

    • GPU:一个类似于CPU的专门用来处理Graphics的处理器, 作用用来帮助加快格栅化操作,当然,也有相应的缓存数据(例如缓存已经光栅化过的bitmap等)机制。

    • OpenGL ES是手持嵌入式设备的3DAPI,跨平台的、功能完善的2D和3D图形应用程序接口API,有一套固定渲染管线流程. 附相关OpenGL渲染流程资料

    • DisplayListAndroid把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。

    • 格栅化 是 将图片等矢量资源,转化为一格格像素点的像素图,显示到屏幕上,过程图如下.

    格栅化操作

    • 垂直同步VSYNC:让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知GPU在载入新帧之前,要等待屏幕绘制完成前一帧。下面的三张图分别是GPU和硬件同步所发生的情况,Refresh Rate:屏幕一秒内刷新屏幕的次数,由硬件决定,例如60Hz.而Frame Rate:GPU一秒绘制操作的帧数,单位是30fps,正常情况过程图如下.

    正常情况


    2.渲染机制分析

    渲染流程线

    UI对象—->CPU处理为多维图形,纹理 —–通过OpeGL ES接口调用GPU—-> GPU对图进行光栅化(Frame Rate ) —->硬件时钟(Refresh Rate)—-垂直同步—->投射到屏幕

    图片名称

    渲染时间线

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

    正常情况

    这里写图片描述

    渲染超时,计算渲染时间超过16ms

    当这一帧画面渲染时间超过16ms的时候,垂直同步机制会让显示器硬件 等待GPU完成栅格化渲染操作,
    这样会让这一帧画面,多停留了16ms,甚至更多.这样就这造成了 用户看起来 画面停顿.

    当GPU渲染速度过慢,就会导致如下情况,某些帧显示的画面内容就会与上一帧的画面相同

    GPU超时情况


    3.渲染时会出现的问题

    GPU过度绘制

    GPU的绘制过程,就跟刷墙一样,一层层的进行,16ms刷一次.这样就会造成,图层覆盖的现象,即无用的图层还被绘制在底层,造成不必要的浪费.

    这里写图片描述

    过度绘制查看工具

    在手机端的开发者选项里,有OverDraw监测工具,调试GPU过度绘制工具,
    其中颜色代表渲染的图层情况,分别代表1层,2层,3层,4层覆盖.

    这里写图片描述

    我的魅族手机的Monitor GPU Rendering

    这里写图片描述

    计算渲染的耗时

    任何时候View中的绘制内容发生变化时,都会重新执行创建DisplayList,渲染DisplayList,更新到屏幕上等一 系列操作。这个流程的表现性能取决于你的View的复杂程度,View的状态变化以及渲染管道的执行性能。

    举个例子,当View的大小发生改变,DisplayList就会重新创建,然后再渲染,而当View发生位移,则DisplayList不会重新创建,而是执行重新渲染的操作.

    当你的View过于复杂,操作又过于复杂,就会计算渲染时间超过16ms,产生卡顿问题.

    渲染耗时呈现工具

    工具中,不同手机呈现方式可能会有差别.分别关于StatusBar,NavBar,激活的程序Activity区域的GPU Rending信息。激活的程序Activity区域的GPU Rending信息。

    界面上会滚动显示垂直的柱状图来表示每帧画面所需要渲染的时间,柱状图越高表示花费的渲染时间越长。

    中间有一根绿色的横线,代表16ms,我们需要确保每一帧花费的总时间都低于这条横线,这样才能够避免出现卡顿的问题。

    这里写图片描述

    每一条柱状线都包含三部分,
    蓝色代表测量绘制Display List的时间,
    红色代表OpenGL渲染Display List所需要的时间,
    黄色代表CPU等待GPU处理的时间。

    这里写图片描述


    4. 如何优化

    有人会说这些小地方,不值得优化.但是当你用的是低配机器,内存到饱和,CPU运算到达饱和,就像一个界面要做很多交互,绘制,加载图片,请求网络.后,一个小问题就会导致页面卡顿(就像我手机的淘宝客户端…),OOM,项目崩溃.

    是的,这就是力量~

    Android系统已经对它优化

    在Android里面那些由主题所提供的资源,例如Bitmaps,Drawables都是一起打包到统一的Texture纹理当中,然后再传递到 GPU里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的。

    我们要做的优化

    扁平化处理,防止过度绘制OverDraw

    1.每一个layout的最外层父容器 是否需要?

    这里写图片描述

    2.布局层级优化

    进行检测时,可能会让多种检测工具冲突,用Android Device Monitor的时候,最好关闭相关手机上的开发者检测工具开关.
    查看自己的布局,深的层级,是否可以做优化.
    渲染比较耗时(颜色就能看出来),想办法能否减少层级以及优化每一个View的渲染时间.

    Hierarchy Viewer工具

    他是查看耗时情况,和布局树的深度的工具.

    这里写图片描述

    3.图片选择

    Android的界面能用png最好是用png了,因为32位的png颜色过渡平滑且支持透明。jpg是像素化压缩过的图片,质量已经下降了,再拿来做9path的按钮和平铺拉伸的控件必然惨不忍睹,要尽量避免。

    对于颜色繁杂的,照片墙纸之类的图片(应用的启动画面喜欢搞这种),那用jpg是最好不过了,这种图片压缩前压缩后肉眼分辨几乎不计,如果保存成png体积将是jpg的几倍甚至几十倍,严重浪费体积。

    4.清理不必要的背景

    这里写图片描述

    5.当背景无法避免,尽量用Color.TRANSPARENT

    因为透明色Color.TRANSPARENT是不会被渲染的,他是透明的.

    
    
    //优化前
    //优化前: 当图片不为空,ImageView加载图片,然后统一设置背景
    Bean bean=list.get(i);
     if (bean.img == 0) {
                Picasso.with(getContext()).load(bean.img).into(holder.imageView);
            }
            chat_author_avatar.setBackgroundColor(bean.backPic);
    
    //优化后
    //优化后:当图片不为空,ImageView加载图片,并设置背景为TRANSPARENT;
    //当图片为空,ImageView加载TRANSPARENT,然后设置背景为无照片背景
    Bean bean=list.get(i);
     if (bean.img == 0) {
                Picasso.with(getContext()).load(android.R.color.transparent).into(holder.imageView);
                holder.imageView.setBackgroundColor(bean.backPic);
            } else {
                Picasso.with(getContext()).load(bean.img).into(holder.imageView);
                holder.imageView.setBackgroundColor(Color.TRANSPARENT);
            }
    

    ————-对比结果——————–
    这里写图片描述

    6.优化自定义View的计算

    View中的方法OnMeasure,OnLayout,OnDraw.在我们自定义View起到了决定作用,我们要学会研究其中的优化方法.

    学会裁剪掉View的覆盖部分,增加cpu的计算量,来优化GPU的渲染

    
      /**
         * Intersect the current clip with the specified rectangle, which is
         * expressed in local coordinates.
         *
         * @param left   The left side of the rectangle to intersect with the
         *               current clip
         * @param top    The top of the rectangle to intersect with the current clip
         * @param right  The right side of the rectangle to intersect with the
         *               current clip
         * @param bottom The bottom of the rectangle to intersect with the current
         *               clip
         * @return       true if the resulting clip is non-empty
         */
        public boolean clipRect(float left, float top, float right, float bottom) {
            return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom,
                    Region.Op.INTERSECT.nativeInt);
        }
    

    5.总结

    性能优化其实不仅仅是一种技术,而是一种思想,你只听过它的高大上,却不知道它其实就是各个细节处的深入研究和处理.

    当然,有的时候也需要自己进行权衡效果和性能,根据需求进行选择.

    还有,Android Device Monitor 是个好东西~简直就是性能优化大本营,性能优化的工具基本都在其中.

    所以在平时的开发过程中,养成良好的思考习惯,是第一步~

    写代码的时候要想:
    1.你的代码是不是多余?
    2.你的对象有没有必要在循环中创建?
    3.你的计算方法是不是最优?

    画界面的时候要想:
    1.布局是否有背景?
    2.是否可以删掉多余的布局?
    3.自定义View是否进行了裁剪处理?
    4.布局是否扁平化,移除非必需的UI组?

    最后,Android Device Monitor 是个好东西~ 性能优化的工具基本都在其中.

  • 相关阅读:
    J
    I题
    H
    G
    F题
    E题
    D题
    C题
    B题
    A题
  • 原文地址:https://www.cnblogs.com/ldq2016/p/6668148.html
Copyright © 2020-2023  润新知