Android性能优化(二)布局渲染流程优化

穿精又带淫゛_ 提交于 2020-02-27 02:47:18

布局渲染流程优化

一、CPU与GPU绘制原理

CPU 的任务繁多,做逻辑计算外,还要做内存管理、显示操作,因此 在实际运算的时候性能会大打折扣,在没有 GPU 的时代,不能显示复 杂的图形,其运算速度远跟不上今天复杂三维游戏的要求。即使 CPU 的工作频率超过 2GHz 或更高,对它绘制图形提高也不大。这时 GPU 的设计就出来了。

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

二、卡顿原理分析

1、当一帧的画面渲染超过16ms的时候,我们就会感觉到卡顿。

首先我们要先明确,我们的手机频率为60FPS,意思为我们的手机会在1秒内刷新60次,也就是约16ms刷新一次。
Android 系统每隔 16ms 发出 VSYNC 信号 (1000ms/60=16.66ms) ,触发对 UI 进行渲染, 如果每次渲染都成 功这样就能够达到流畅的画面所需要的 60fps ,为了能够实现 60fps ,这意味着计算渲染的大多数操作都必须 在 16ms 内完成。
这就仿佛我们的手机会16ms发送一趟列车,如果你没有坐上只能等下一趟。
如果错过了,比如说我们花费34ms才完成计算,那么就会出现我们称之为丢帧的情况。

2、16 毫秒的时间主要被两件事情所占用 :

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

3、得出初步解决思路
CPU减少xml转化成对象的时间
GPU减少重复绘制的时间

三、什么是过度绘制

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

1、GPU过度绘制的几种情况:
1)、自定义控件中 onDraw 方法做了过多的重复绘制
2)、布局层次太深,重叠性太强。用户看不到的区域GPU也会渲染,导致耗时增加。

2、过度绘制查看工具
在手机端的开发者选项里,有overDraw监测工具,调试GPU过度绘制工具,其中颜色代表图层渲染情况。

蓝色:过度绘制一次,无过度绘制;
淡绿:过度绘制两次;
淡红:过度绘制三次;
深红:过度绘制四次;

代表4种不同程度overDraw情况,我们的目的就是尽量减少红色,看到更多的蓝色区域。

四、Android系统所做的优化

CPU 转移到 GPU 是一件很麻烦的事情,所幸的是 OpenGL ES 可以把那些需要渲染的纹理 Hold 在 GPU Memory 里面,在下次需要渲染的时候直接进行操作。所以如果你更新了 GPU 所 hold 住的纹理内容,那么之前保存的状态就丢失了。
在 Android 里面那些由主题所提供的资源,例如 Bitmaps ,Drawables 都是一起打包到统一的 Texture 纹理当中,然后再传 递到 GPU 里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的。当然随着 UI 组件的越来 越丰富,有了更多演变的形态。例如显示图片的时候,需要先经过 CPU 的计算加载到内存中,然后传递给 GPU 进行渲染。 文字的显示比较复杂,需要先经过 CPU 换算成纹理,然后交给 GPU 进行渲染,返回到 CPU 绘制单个字符的时候,再重新 引用经过 GPU 渲染的内容。动画则存在一个更加复杂的操作流程。
为了能够使得 App 流畅,我们需要在每帧 16ms 以内处理完所有的 CPU 与 GPU 的计算,绘制,渲染等等操作。

五、App层如何优化GPU过度绘制

1,去掉默认背景
1>. 如果在布局的Layout中设置background,禁用主题的背景可以少绘制一层。
2>. 在Activity上嵌套的Fragment如果背景与Activity一致也可以去掉背景。
3>. 检查每一个容器与内部的item与外层是否有重复使用的颜色部分,也可以去掉每一个容器与item的背景
2、如果是自定义view,可以去掉不用展示的部分
绘制的图片如果有重叠的部分,就像扑克牌一样,有被盖住的部分,我们可以通过截取画板的形式,只绘制该图片显示区域的大小。
可使用canvas.clipRect()方法
比如:

public class DroidCardsView extends View {

    //图片与图片之间的间距
    private int mCardSpacing = 150;
    //图片与左侧距离的记录
    private int mCardLeft = 10;

    private List<DroidCard> mDroidCards = new ArrayList<DroidCard>();

    private Paint paint = new Paint();

    public DroidCardsView(Context context) {
        super(context);
        initCards();
    }

    public DroidCardsView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initCards();
    }



    /**
     * 初始化卡片集合
     */
    protected void initCards(){
        Resources res = getResources();
        mDroidCards.add(new DroidCard(res, R.drawable.alex,mCardLeft));

        mCardLeft+=mCardSpacing;
        mDroidCards.add(new DroidCard(res, R.drawable.claire,mCardLeft));

        mCardLeft+=mCardSpacing;
        mDroidCards.add(new DroidCard(res, R.drawable.kathryn,mCardLeft));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /*重点关注*/
        for (int i = 0; i < mDroidCards.size() - 1; i++){
            drawDroidCard(canvas, mDroidCards,i);
        }

        drawLastDroidCard(canvas,mDroidCards.get(mDroidCards.size()-1));
        invalidate();
    }

    /**
     * 绘制最后一个DroidCard
     * @param canvas
     * @param c
     */
    private void drawLastDroidCard(Canvas canvas,DroidCard c) {
        canvas.drawBitmap(c.bitmap,c.x,0f,paint);
    }

    /**
     * 绘制DroidCard
     * @param canvas
     * @param mDroidCards
     * @param i
     */
    private void drawDroidCard(Canvas canvas,List<DroidCard> mDroidCards,int i) {
    	/*重点关注*/
        DroidCard c = mDroidCards.get(i);
        canvas.save();
        canvas.clipRect((float)c.x,0f,(float)(mDroidCards.get(i+1).x),(float)c.height);
        canvas.drawBitmap(c.bitmap,c.x,0f,paint);
        canvas.restore();
    }
}

六、App层如何优化CPU纹理化:减少嵌套

1、布局优化工具:Hierarchy view
在Android Studio中打开 tool ==》Hierarchy view进行调试。
Hierarchy view的所显示的树状图DecorView分为三部分:statusBarnavigationBar,还有一个填充着我们布局的LinerLayout
所以我们能优化的部分只有总的填充我们布局的LinerLayout部分。
2、优化思路
1>. 用merge标签替换FrameLayout,避免与父容器重叠。
2>. 如果在Hierarchy view的所显示的树状图中某一个layout后面只有一个分支,那么它就可以被干掉,减少一层嵌套。
3>. 尽量减少LinerLayoutRelativeLayout等与之同级别大layout之间的互相嵌套。
4>. 在大容器内尽量只嵌套类似于TextView这种的子布局

七、性能优化主要思想

性能优化其实不仅仅是一种技术,而是一种思想,它其实就是各个细节处的深入研究和处理,和挤牙膏一样。优化时主要有以下思路:
1、布局内是否有背景。

2、是否可以删除多个布局。(尽量容器内只有一个子节点)(适时使用ScroView代替linerLayout)。
3、自定义View是否进行了裁剪(适时使用include加载多次重绘的子布局)。
4、在Hierarchy view的所显示的树状图上观察布局是否扁平化。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!