美文网首页
UI绘制优化

UI绘制优化

作者: ArcherZang | 来源:发表于2019-04-29 14:05 被阅读0次
  1. UI绘制优化好处,不仅可以减少卡顿,也可以加快启动速度,减少不必要的内存和cpu资源消耗。
  2. UI绘制主要工作就是减少CPU和GPU的工作量。
View绘制到屏幕流程

具体解说CPU与GPU绘制文章
简单描述:CPU加载xml转成对象后计算出向量图形,交给GPU光栅化,最后GPU交给硬件合成器决定最终显示在显示器的画面。
(1)首先应用主线里里的每个view都会经过老三部:measure,layout,draw.然后TextView,Button等等控件通过CPU计算转换为内存中的polygons(多边图形)和texture(纹理)。
(2)其次,CPU通过OpenGL的接口将纹理数据传递给GPU渲染处理,由于图形API不允许CPU直接与GPU通信,而是通过中间图形驱动层来连接两部分,驱动层维护了一个队列,CPU把display list添加到队列中,GPU从这个队列去除数据进行绘制,最终在屏幕上显示出来;在这个过程中,每个View都有一个DisplayList,由DisplayList这个结构负责保存绘制用到的所有信息,在Displaylist无需重新创建或改变的情况下,GPU可以直接使用这里的数据进行渲染.当View中的某些可见组件,那么之前的DisplayList就无法继续使用了,我们需要回头重新创建一个DisplayList并且重新执行渲染指令并更新到屏幕上。而不是像软件绘制那样需要向上递归。这样可以大大减少绘图的操作数量,因而提高了渲染效率.
(3)最后,GPU对图形数据进行渲染,通过匿名共享内存:SharedClient把需要显示的数据传到SurfaceFlinger;一个SharedClient对应一个Android应用程序,一个SharedClient可以创建31个SharedBufferStack;每个SharedBufferStack对应一个Surface,也就是一个Window;每个SharedBufferStack包含的BufferQueue内部都有三个Graphic Buffer,两个用于绘制一个用于显示.我们会把内容先绘制到一个后置缓冲区(OffScreen Buffer或者Back Buffer),在另外一个绘制下一帧;在需要显示时,才把离屏缓冲区的内容通过SwapBuffer驱动复制到Front Graphic Buffer中, 通过它将栅格化的信息交给SurfaceFlinger,SurfaceFlinger通过创建维护Layer再交给Hareware Composer,它会根据Layer中的位置、Z-Order顺序等信息合成为最终屏幕需要显示的内容,而这个内容会交给系统的帧缓冲区Frame Buffer来显示(Frame Buffer是非常底层的,可以 理解为屏幕显示的抽象).
(4)垂直同步VSYNC 60fps: 让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知GPU在载入新帧之前,要等待屏幕绘制完成前一帧。如果下一帧周期到是屏幕前一帧没有绘制结束,后置缓冲区不能清空,多出来的一个后置缓冲区就可以用来绘制。

DisplayList:DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。
Refresh Rate:屏幕在一秒时间内刷新屏幕的次数----有硬件的参数决定,比如60HZ.
Frame Rate:GPU在一秒内绘制操作的帧数,比如:60fps。
60fps:在与手机交换过程中,如果触摸及反馈 60帧以下人是能感觉出来的。60帧以上不能察觉变化。当低于60帧时感觉画面卡顿和迟滞现象
栅格化:将向量图形格式表示的图像转换成位图以用于显示器,即把button、textview等组件拆分到不同的像素上进行显示。这是一个很费时的操作,GPU的引入就是为了加快栅格化的操作。

过度绘制优化(主要减少GPU工作量)
  1. 过度绘制检测工具
    开发者选项-》Debug GPU overdraw/调试GPU过度绘制
    白色: 无过度绘制
    蓝色: 过度绘制一次,正常一层背景和一层字体或按钮
    淡绿: 过度绘制两次
    淡红: 过度绘制三次
    深红: 过度绘制四次或者四次以上
    主要减少红色
  2. GPU过度绘制几种情况:
        (1). 自定义控件中 onDraw方法做了过多重复绘制
        (2). 布局层次太深,重叠性太强。用户看不到的区域GPU也会渲染,导致耗时增加。
        (3). View透明色属性即Alpha属性的使用,因为会在缓冲区渲染两次。
  3. 过度绘制优化:
    (1) 减少背景重复:非业务需要,不要去设置背景
        去掉所有activity主题设置中的默认背景
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
    <item name="android:windowBackground">@null</item>
</style>

        去掉单个activity的主题设置的属性:可以在setContentView之前getWindow().setBackgroundDrawable(null);

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/gray5" 
    android:orientation="vertical"
    tools:context="com.lqr.wechat.activity.MainActivity">
<!--去掉android:background="@color/gray5" -->

        去除和列表背景色相同的Item背景色;
        若页面背景色与通用背景色不一致,在页面渲染完成后移除窗口和Decorview背景色;
当背景无法避免,但是某些情况下背景看不见,比如有图片时背景看得见,使用占位图时背景看不见,或者imageview同时存在有背景无图、无背景有图,设置图或者背景透明色Color.TRANSPARENT,因为透明色不渲染。

(2) 使用裁减减少控件之间的重合部分
clipRect()该方法用于裁剪画布,只会显示被裁剪的区域,之外的区域将不会显示。
该方法最后有一个参数Region.Op,表示与之前区域的区域间运算种类,如果没有这个参数,则默认为Region.Op.INTERSECT
这几个参数的意义为:
• DIFFERENCE是第一次不同于第二次的部分显示出来
• REPLACE是显示第二次的
• REVERSE_DIFFERENCE 是第二次不同于第一次的部分显示
• INTERSECT交集显示
• UNION全部显示
• XOR补集 就是全集的减去交集生育部分显示
注意:
clipxx方法只对设置以后的drawxx起作用,已经画出来的图形,是不会有作用的


优化前.jpg
优化代码.jpg
优化后.jpg

(3) 不在屏幕的元素尽量使用Canvas.quickReject把他们给剔除
(4) 占位View使用Space,CPU会计算,onDraw() 方法空不会交给GPU计算。
(5) TextView – 使用setTextColor()方法替代setAlpha()。这种方法使用Alpha通道来改变字色,字也会直接使用它进行绘制。
ImageView – 使用setImageAlpha()方法替代setAlpha()。原理同上。
自定义控件 – 如果你的自定义控件不存在过度绘制,那就开启硬件加速。
外边框都被切掉了,除非hasOverlappingRendering() 返回false。也可用view.getBackground.setAlpha(int int)设置透明度,则能够显示出正常边框

布局和View优化(主要减少CPU工作量)

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

  1. 布局检测工具
  • Android Device Monitor窗口中Hierarchy view
    显示View的Measure, Layout和Draw花费时间。
    三个点也是代表着View的Measure, Layout和Draw。
    绿:表示该View的此项性能比该View Tree中超过50%的View都要快;例如,代表Measure的是绿点,意味着这个视图的测量时间快于树中的视图对象的50%。
    黄:表示该View的此项性能比该View Tree中超过50%的View都要慢;
    红:表示该View的此项性能是View Tree中最慢的
    微信截图_20191028102549.png
  • AndroidStudio的Tools中Layout Inspector
    运行时检查其布局的详细信息。布局是在运行时构建的,不是完全用xml构建的,也会显示;方便对比分析和定位问题。
    微信截图_20191028112805.png
  • AndroidStudio的Tools中Layout Inspector
    开发者选项-》Profile GPU rendering/分析GPU表现
    Profile GPU renderin工具以滚动直方图的形式显示,相对于每帧16毫秒的基准,呈现ui窗口的帧需要多长时间。在功能较弱的gpu上,可用的填充率(GPU可以填充帧缓冲区的速度)可能非常低。随着绘制帧所需的像素数的增加,GPU可能需要更长的时间来处理新命令,并要求系统的其他部分等待,直到它能够赶上。分析工具可以帮助您识别GPU在试图绘制像素时是否被压垮,或者是否被过度渲染所累。
    注意:此分析工具不适用于使用ndk的应用程序。这是因为每当OpenGL获取一个full-screen的context时,系统都会将框架消息推送到后台。在这种情况下,您可能会发现GPU制造商提供的分析工具很有帮助。
    工具效果.PNG
    Android6.0以上颜色说明.png
    Android4.0到5.0颜色说明.png
  • Systrace
    Systrace是一个platform-provided工具,用于记录设备在短时间内的活动。 允许在系统级别上收集和检查设备上运行的所有进程的时间信息。将来自Android内核的数据(如CPU调度程序、磁盘活动和应用程序线程)结合起来生成一个HTML报告,帮助确定如何最好地提高应用程序或游戏的性能。该报告突出了它观察到的问题(如在显示动作或动画时的ui jank),并且提供了有关如何修复这些问题的建议。但是,Systrace不会在应用程序进程中收集有关代码执行的信息。可以通过自定义事件收集某个方法的执行时间包含native层,但是不显示具体执行信息包括执行了哪些函数。
    Java:
    Trace.beginSection("MyAdapter.onCreateViewHolder");  
          MyViewHolder myViewHolder;  
          try  { 
             myViewHolder =  MyViewHolder.newInstance(parent); 
          }  finally  {  
              // In 'try...catch' statements, always call `[endSection()](https://developer.android.google.cn/reference/android/os/Trace.html#endSection())`  
              // in a 'finally' block to ensure it is invoked even when an exception  // is thrown.  
    Trace.endSection(); 
    
    C:
    为ATrace functions定义方法指针
    #include <android/trace.h>
    #include <dlfcn.h>
    
    void *(*ATrace_beginSection) (const char* sectionName);
    void *(*ATrace_endSection) (void);
    
    typedef void *(*fp_ATrace_beginSection) (const char* sectionName);
    typedef void *(*fp_ATrace_endSection) (void);
    
    在运行时加载ATrace符号,通常在对象构造函数中执行这个过程,出于安全原因,仅在应用程序或游戏的调试版本中包含对dlopen()的调用。
    // Retrieve a handle to libandroid.
     void *lib = dlopen("libandroid.so", RTLD_NOW || RTLD_LOCAL);
     // Access the native tracing functions.
     if (lib != NULL) {
     // Use dlsym() to prevent crashes on devices running Android 5.1
     // (API level 22) or lower.
      ATrace_beginSection = reinterpret_cast<fp_ATrace_beginSection>(dlsym(lib, "ATrace_beginSection"));
      ATrace_endSEction = reinterpret_cast<fp_ATrace_endSection>(dlsym(lib, "ATrace_endSection"));
     } 
    
定义自定义事件
  • AndroidStudio--Profiler
    提供实时数据帮助你理解你的APP是怎么样使用CPU、Memeory、Network、Battery Resources,主要看CPU分析界面一段时间内method如何调用和执行时间
    Profiler.PNG
  • adb shell dumpsys gfxinfo
  1. 布局和View优化几种情况:
    (1)布局过于复杂,无法在16ms内完成渲染
    布局冗余、多层linearlayout嵌套、相对布局多次测量、业务复杂度高
    (2)布局内容重复加载;listview和recycleview一直加载布局文件或者在onResume、onStart、onPostResume中加载布局
    (3)离屏内容的绘制,Nav Drawer里面不可见的View系统会优化但不是所有,
    (4)自定义View频繁的触发measure、layout,导致measure、layout累计耗时过多
    (5)view频繁的重新渲染,包括频繁的设置可见与不可见方法,这样也会调用
    (6)在主线程进行耗时操作,导致UI线程卡顿例如请求网络、IO读取数据
    (7)内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作
    频繁GC的原因:(1) 内存抖动(Memory Churn), 即大量的对象被创建又在短时间内马上被释放。(2) 瞬间产生大量的对象会严重占用 Young Generation 的内存区域, 当达到阀值, 剩余空间不够的时候, 也会触发 GC。即使每次分配的对象需要占用很少的内存,但是叠加在一起会增加 Heap 的压力, 从而触发更多的 GC。
    (8)同一时间动画执行次数过多,导致CPU或者GPU负载过重
    (9)View透明色属性即Alpha属性的使用,因为会在缓冲区渲染两次。

3.布局优化方案
(1)能在一个平面显示的内容,尽量只用一个容器,根据布局特点选择对应的布局,
尽可能少用wrap_content,wrap_content 会增加布局 measure 时计算成本,
尽量减少ConstraintLayout和RelativeLayout中太多无效依赖,可以减少不必要控件的刷新,
删除控件中无用的属性。

3864402-a40475789fa1938e.png
(2)使用merge尽可能把相同的容器合并,可以配合include使用
(3)删除冗余视图,比如TextView提供drawableLeft添加图片替代imageview
        使用 SpannableStringBuilder替换多个texview,多种不同大小、颜色或者图文混排需要显示时,我们往往会利用多个TextView来进行组合,但是某些效果通过一个TextView就可以实现。
        Android中各种Span的用法
        使用LinearLayout自带的divider属性实现分割线,而不是在布局中手动添加一个额外的View作为分割线
        与分割线相关的属性包括以下几个:
        divider:传入分割线的drawable,可以是一个图片,也可以是自己通过xml实现的drawable。
        showDividers:分割线显示的位置,beginning/middle/end,分割对应头部、中间、尾部。
        dividerPadding:分割线距离两边的间距
(4)ListView和Recycleview,可以重复使用item,注意不要在更新内容的地方加载布局,局部刷新如 RecyclerView 的 DiffUtil
(5)代码上能复用View的尽量复用,延时加载或者普通加载XML布局不要在反复调用的方法中使用
(6)对于业务复杂的布局可以使用延时加载的地方尽量使用,比如ViewStub,业务上要减少非必须业务控件,以及减少对离屏内容的绘制
(7)减少对requestlayout和invalidate()频繁调用,View还可以调用局部刷新方法invalidate(int left, int top, int right, int bottom) ,自定义view要减少对子view的重复measure ,不要做复杂的绘图操作,重复内容需要剪裁,
(8)不要在onMeasure、onLayout和onDraw和activity声明周期方法中做耗时操作,尽量放到子线程。
(9)用 SurfaceView 或 TextureView 代替普通 View。SurfaceView 或 TextureView 可以通过将绘图操作移动到另一个单独线程上提高性能
(10) 尽量为所有分辨率创建资源,减少不必要的硬件缩放,这会降低 UI 的绘制速度。这个和APK大小之间要做取舍。
(11)space 经常用于组件之间的缝隙,其draw()为空,Space 相对于View设置间距的好处是不用draw,缺点是不能设置背景增加了布局中的View。
(12)尽量少在onMeasure、onLayout、onDraw和for循环中创建对象,防止频繁GC
(13) 合理使用动画,尽量使用属性动画,减少了自身的重绘。某些情况下可以用硬件加速方式来提供流畅度,或者采用自定义view代替动画,最后记得在Activity的onStop()方法中调用Animation.cancle()进行动画停止。
启用硬件加速
 Application 级别
 <application android:hardwareAccelerated="true" />
 Activity 级别
 <activity android:hardwareAccelerated="true" />
 Window 级别
 getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
 View 级别
 // 如果是 software,会将 View 绘制到一个 Bitmap,
 // 然后依然是通过硬件加速将 Bitmap 绘制到 Canvas
 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
 LAYER_TYPE_NONE:普通渲染方式,不会返回一个离屏的缓冲,默认值。
 LAYER_TYPE_HARDWARE:如果这个应用使用了硬件加速,这个 View 将会在硬件中渲染为 硬件纹理,
 如果应用程序并没有被硬件加速,则其效果和     LAYER_TYPE_SOFTWARE 相同。
 LAYER_TYPE_SOFTWARE:此 View 通过软件渲染为一个 Bitmap。
 检查是否开启了硬件加速
 view.isHardware-Accelerated();
 Canvas.isHardwareAccelerated();
 如果 View 中要处理中文长文本,需要关闭硬件加速。因为每个中文编码不一样,效果不理想。

(14)RenderThread与RenderScript
在Android 5.0,系统增加了RenderThread,对于ViewPropertyAnimator和CircularReveal动画,我们可以使用RenderThead 实现动画的异步渲染。当主线程阻塞的时候,普通动画会出现明显的丢帧卡顿,而使用RenderThread渲染的动画即使阻塞了 主线程仍不受影响.
现在越来越多的应用会使用一些高级图片或者视频编辑功能,例如图片的高斯模糊、放大、锐化等.拿 "扫一扫"这个场景来看,这里涉及大量的图片变换操作,例如缩放、裁剪、二值化以及降噪等. 图片的变换涉及大量的计算任务,根据我们说到的,这个时候使用GPU是更好的选择. 我们可以通过RenderScript,它是Android操作系统上的一套API.它基于异构计算思想,专⻔用于密集型计算.
RenderScript 提供了三个基本工具:一个硬件无关的通用计算API;一个类似于CUDA、OpenCL和GLSL的计算API;一个类C99的脚本语 言.允许开发者以较少的代码实现功能复杂且性能优越的应用程序


16dc962f1ba3a91c.png
  • 总结:
    性能优化其实不仅仅是一种技术,而是一种思想,你只听过它的高大上,却不知道它其实就是各个细节处的深入研究和处理。就像挤牙膏一样。

相关文章

  • 性能优化

    Android UI性能优化实战 识别绘制中的性能问题性能优化(二) UI 绘制优化 通过Hierarchy Vi...

  • Android UI性能优化

    Ui性能优化 参考博客:Android UI性能优化实战 识别绘制中的性能问题Android UI性能优化详解 1...

  • UI优化

    UI优化,也就是体验优化 常见优化场景 1) 过渡绘制 2) 布局复杂度 3) 逻辑优化 4) 内存使用优化 UI...

  • Android UI渲染优化

    Android UI渲染优化 1. 过度绘制优化 什么是过度绘制? Overdraw(过度绘制)描述的是屏幕上的某...

  • android性能优化全方面解析(二)

    补昨天UI优化后半段,前半段点开主页就有啦 一 绘制优化 那么什么是绘制优化?绘制优化主要是指View的Ondra...

  • Android App优化

    本篇文章来简单介绍一下App优化的方式。 1、UI优化 UI优化主要是提高UI的绘制效率,包括减少UI层次,提高初...

  • IOS - UIView绘制流程 (displayLayer)(

    性能优化之 UI渲染优化 - 异步渲染 使用displayLayer进行异步绘制

  • 无标题文章

    APP性能优化 UI卡顿优化 View的绘制原理 UI卡顿原理分析 UI卡顿检测分析 BlockCanary原理分...

  • UI绘制优化

    60Hz 刷新频率由来 12 fps :由于人类眼睛的特殊生理结构,如果所看画面之帧率高于每秒约 10-12 帧的...

  • UI绘制优化

    CPU和GPU 名称介绍作用CPU处理器将 UI 对象转换为一系列多边形和纹理(计算画图的方法,可以理解为通过公式...

网友评论

      本文标题:UI绘制优化

      本文链接:https://www.haomeiwen.com/subject/dytdnqtx.html