zoukankan      html  css  js  c++  java
  • 性能优化-渲染机制及优化

    在手机上显示图片,播放视频,这是很常见的手机操作,也就是屏幕的绘制在软件开发中几乎是每个应用都会打交道的,这篇文章记录了渲染机制以及如何做优化

    卡顿产生的原因

    • 在Activity中直接进行网络访问/大文件的IO操作还有就是自定义的View没有优化好,以上的情况都有可能造成卡顿,甚至是无响应

    • 当产生大量的垃圾时,GC回收大量垃圾的时候,也会造成卡顿

    • Android每隔16ms就会重绘一次Activity,也就是说必须在16ms以内完成屏幕刷新所要求的参数设置,之所以是16ms,那是因为大多说手机的刷新率是60Hz,那么也就是说1000ms/60=16.66ms,如果在这个时间内,没有完成参数设置,那么就会产生丢帧现象,在视觉上体现出来就是卡顿

    内存抖动造成卡顿分析及解决办法

    短时间内分配大量内存,会造成UI线程短时间阻塞,此时如果在绘制屏幕,那么此时就会造成卡顿
    例如在绘制屏幕的时候有如下计算执行。那么此时会造成卡顿现象

    /**
     * 排序后打印二维数组,一行行打印
     */
    public void imPrettySureSortingIsFree() {
        int dimension = 300;
        int[][] lotsOfInts = new int[dimension][dimension];
        Random randomGenerator = new Random();
        for(int i = 0; i < lotsOfInts.length; i++) {
            for (int j = 0; j < lotsOfInts[i].length; j++) {
                lotsOfInts[i][j] = randomGenerator.nextInt();
            }
        }
    
        for(int i = 0; i < lotsOfInts.length; i++) {
            String rowAsStr = "";
            //排序
            int[] sorted = getSorted(lotsOfInts[i]);
            //拼接打印
            for (int j = 0; j < lotsOfInts[i].length; j++) {
                rowAsStr += sorted[j];
                if(j < (lotsOfInts[i].length - 1)){
                    rowAsStr += ", ";
                }
            }
            Log.i(TAG, "Row " + i + ": " + rowAsStr);
        }
    }
    
    public int[] getSorted(int[] input){
    	int[] clone = input.clone();
    	Arrays.sort(clone);
    	return clone;
    }
    

    上述代码在第二个for循环中,大量创建对象并弃用,产生了大量的垃圾,此时GC回收垃圾,占用主线程,直接就导致了GC回收占用时间加上屏幕绘制时间高于16ms,此时就发生了卡顿,其卡顿时间取决于GC的工作时间,故在主线程计算这种数据的时候(这种情况放在子线程最好),要尽量减少内存的分配,防止内存抖动,从而有效避免卡顿现象的发生
    其改进方法就是使用StringBuilder,减少内存的分配

    StringBuilder sb = new StringBuilder();
    String rowAsStr = "";
    for(int i = 0; i < lotsOfInts.length; i++) {
    	// 清除上一行
    	sb.delete(0, rowAsStr.length());
    	//排序
    	int[] sorted = getSorted(lotsOfInts[i]);
    	//拼接打印
    	for (int j = 0; j < lotsOfInts[i].length; j++) {
    		sb.append(sorted[j]);
    		if(j < (lotsOfInts[i].length - 1)){
    			sb.append(", ");
    		}
    	}
    	rowAsStr = sb.toString();
    	Log.i(TAG, "Row " + i + ": " + rowAsStr);
    }
    

    计算性能占用CPU造成卡顿及解决办法

    有时候在执行函数的时候,会占用大量CPU资源,尤其是递归函数的执行过程,会占用CPU大量时间,那么此时也有可能造成卡顿
    例如在主线程执行斐波那契梳理的递归实现时,如果此时在执行屏幕绘制,那么也会造成卡顿

    public int computeFibonacci(int pos) {
        if (pos <= 2) {
            return 1;
        } else {
            return computeFibonacci(pos - 1) + computeFibonacci(pos - 2);
        }
    }
    

    使用TraceView观察到,这个函数在执行的时候占用了CPU大量的时间,造成了卡顿
    这种情况采用批处理和缓存思想,储存运算结果,更新数据来得到最终结果,避免递归调用造成的大量CPU时间占用

    public int computeFibonacci(int pos) {
        int prev = 0;
        int current = 1;
        int newValue;
        for (int i = 1; i < pos; i++) {
            newValue = current + prev;
            prev = current;
            current = newValue;
        }
        return current;
    }
    

    渲染机制简单介绍

    • Android系统的渲染由两个关键组件构成:CPU和GPU
      在CPU方面,最常见的性能问题是不必要的布局和失效,这些内容必须在视图层次结构中进行测量、清除并重新创建,引发这种问题通常有两个原因:一是重建显示列表的次数太多,二是花费太多时间作废视图层次并进行不必要的重绘,这两个原因在更新显示列表或者其他缓存GPU资源时导致CPU工作过度
      在GPU方面,最常见的问题是我们所说的过度绘制(overdraw),通常是在像素着色过程中,通过其他工具进行后期着色时浪费了GPU处理时间
    • 图像的显示是通过栅格化来完成的,以下的图片说明了栅格化的过程
      栅格化过程
    • CPU首先将布局文件转化为多边形或者纹理,然后交由GPU去负责栅格化
      CPU2GPU
    • 渲染性能的优化就是尽可能地上传数据到GPU,然后尽可能长地在不修改的情况下保存数据,因为每次上传资源到GPU时,我们都会浪费宝贵的处理时间

    GPU存在的主要问题

    • GPU性能强大,然后其常见的一个问题就是过度绘制
      过度绘制:指屏幕上的某个像素点在同一帧的时间内被绘制了多次
    • 那么此时用户在界面上只能看到最上面一层,下面绘制的就属于过度绘制了
      要查看过度绘制很简单,在开发者选项里面打开Show GPU overdraw即可
      然后就能在屏幕上看到多种颜色,这些颜色代表过度绘制的倍数
      过度绘制
      最理想的情况就是全部都是蓝色,因此可以通过这个现象来优化我们的app

    过度绘制的解决办法

    1. 清除不必要的背景和图片
      材料主题会绘制一遍屏幕,我们如果这时候再设置背景颜色,那么会产生2倍过度绘制,此时就需要取消其默认的背景图片,也就是将Activity的背景图片设为null,其方法如下:
    getWindow().setBackgroundDrawable(null);
    
    1. 清除XML文件中,不必要的背景声明

    CPU的工作部分

    • CPU将布局文件转化为GPU能够识别的对象,通过GPU的栅格化,从而显示在屏幕上,这是在DisplayList帮助下完成的,DisplayList将数据传递给GPU,GPU通过OpenGL将屏幕绘制出来
    • 任何时候View的绘制内容发生变化,都需要重新创建DisplayList并重新执行指令更新到屏幕
      CPU的view转化检测
      工具:Hierarchy Viewer检测(在Android Monitor里面)
      选中应用 -> 点击Load the view hierarchy into the tree view
      然后再点击Obtain layout times for tree rooted at selected node
      就会看到如下界面
      布局视图优化前
      三个圆点分别代表:测量、布局、绘制三个阶段的性能表现
      1)绿色:渲染的管道阶段,这个视图的渲染速度快于至少一半的其他的视图
      2)黄色:渲染速度比较慢的50%
      3)红色:渲染速度非常慢
      观察其分布,自己写的布局杂乱无章,深层嵌套,而且红点很多,这样会降低CPU的解析效率
      优化策略:
      当我们的布局是用的FrameLayout的时候,我们可以把它改成merge,可以避免自己的帧布局和系统的ContentFrameLayout帧布局重叠造成重复计算(measure和layout)
      ViewStub:当加载的时候才会占用。不加载的时候就是隐藏的,仅仅占用位置
      优化思想:查看自己的布局,层次是否很深以及渲染比较耗时,然后想办法能否减少层级以及优化每一个View的渲染时间
      布局视图优化后

    总结

    减少CPU计算时间

    CPU的优化,从减轻加工View对象成Polygons和Texture来下手
    View Hierarchy中包涵了太多的没有用的view,这些view根本就不会显示在屏幕上面,一旦触发测量和布局操作,就会拖累应用的性能表现。

    减少CPU将计算好的Polygons和Texture传递到GPU的时间

    OpenGL ES API允许数据上传到GPU后可以对数据进行保存,做了缓存

    减少GPU进行格栅化

    优化:尽量避免过度绘制(overdraw)
    GPU如何优化:

    1. 背景经常容易造成过度绘制
      由于布局设置了背景,同时用到的MaterialDesign的主题会默认给一个背景
      解决的办法:将主题添加的背景去掉

    2. 自定义控件如何处理过度绘制(多张图片有重叠)
      可以通过裁剪来处理canvas.clipRect()
      前n-1张

    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();
    }
    

    第n张

    private void drawLastDroidCard(Canvas canvas,DroidCard c) {
        canvas.drawBitmap(c.bitmap,c.x,0f,paint);
    }
    
  • 相关阅读:
    word删除脚注的方法
    移动最小二乘
    word插入的对象和文字混合到了一起的解决办法
    插值
    中学小学学校学生德育量化管理系统_文明班评比量化系统_德育评价系统_德育量化考核系统_政教管理系统_政教考核系统
    需求的最初形式:12306ng的需求小说
    异步javascript的原理和实现
    需求与设计过程(1)用例
    项目管理沙龙第十二次会议纪要为没有共识的项目组定制敏捷方法
    项目进度控制的技术
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664639.html
Copyright © 2011-2022 走看看