UI性能优化(一)Overdraw排查和调优

Android的渲染机制优化是重中之重,大家都知道,但是问题也往往还是会出现,打造一个高性能扁平化的APP,优化必不可少。

概述与案例

为什么会发现卡顿性能,屏幕过热等问题呢,参考Google 发布 Android 性能优化典范,本文主要用从具体调优技巧出发,实现Android overdraw问题的排查和调优

Overdraw即是绘制重叠,很好理解,App界面都有布局,我们可以认为他就是画上去的,如果我们一遍遍的去涂画背景,底层的不可见涂层便是不必要的,浪费资源的同时也会造成画板的厚重层次。

排查技巧一:查看是否过度绘制

  • 通过在手机开发者选项中打开GPU过度绘制调式即可

  • 打开GPU过度绘制开关后,以下UI界面为例,其中不同颜色对应的屏幕的绘制次数,颜色的深度代表层级,颜色越深就表示UI的层级越重

    • 蓝色 1x
    • 绿色 2x
    • 淡红 3x
    • 深红 4x+ (尽量避免这种情况的出现)

排查技巧二:通过Hierachy View或者Layout Inspecot查看布局层级

Android 的UI加载布局,都是成树状结构,越往分支延伸,层级结构越复杂,搭载一个简单轻薄的界面,需要做到简化布局的目的。

  • Hierachy View在AndroidStudio 3.0后都是通过monitor.bat工具打开调式,在SDK目录下tools里
    参考官方使用工具介绍Profile your layout with Hierarchy Viewer

  • Layout Inspecot通过AndroidStudio即可查看View Tree

Overdraw优化策略-扁平化

过度绘制的优化我个人的原则就是:所见即所画

这句话什么意思呢,就是尽可能的只绘制我们所需要看到的界面,如我概述中说的,如果布局是刷漆,那么刷一层就是我们最理想化的结果。当然,理想化是不可达的,但我们可以无限去接近它。

围绕这个原则做优化,我做了2点概要总结,当然,在具体开发中这4个方向又包含了其他许多优化操作的可能。

一、至尊超薄


顾名思义,就是打造扁平化UI,超薄绘制图层,关键要理解Android App Window层级的联系,如下图,
优化层级,我的常用方法就是从2点出发

  • 背景图层的优化打薄:去除不必要的背景图层,如包括Drawable或者Color的绘制,可以从这几点考虑
  • 减少布局层级:布局层级优化即减少window的控件层级关系,选择最优的控件布局来达到我们的UI效果。

技巧一:去除window背景绘制

我们在定义Activity Layout时,会给Activity指定主题,通常的theme中会默认给DecorView中指定一个背景色,当时但我们Layout中需要自定义背景布局颜色或者图片时,我们可以去除这个不必要的window背景色

	通过在theme中指定 

		android:windowbackground="null"

	或者 在Activity中 setContentView() 后指定

		getWindow().setBackgroundDrawable(null); 

技巧二:上下级同色图层间保持最少图层绘制原则

  1. 如布局中的子View背景色和父控件背景色一致时,可以去除子View的背景绘制或者去除布局背景绘制而选择分别给子View绘制背景,这样保证了最少图层绘制
  2. 如ImageView或者Button的backgroundColor和布局颜色一致时,可以将颜色设置为透明,或者不设置。当然,在selector drawble中也可以使用。

技巧三:学会选择布局控件

Android有很多基本布局,从FrameLayout,LinearLayout,RelativeLayout再到新出的ConstraintLayout,不同的基本布局,应用场景也不一样。

如何选择很重要

以扁平化的UI为目标来看,减少层级是一个目的,同时也要考虑他们绘制时的资源消耗,并不是越新功能越强大的控件就越出色越受用。
理解绘制资源消耗,就得明白布局内部做了些什么,

比如LinearLayout对比RelativeLayout对比,RelativeLayout在measure时耗时更严重,原因是RelativeLayout对子View做了2次measure。但是这样说来,是不是说我就非得多用LinearLayout,不用RelativeLayout呢?不是这样的,理解了LinearLayout内部结构就会发现,当我们使用它的权重属性时,它也会测量2次,而且当多层次的LinearLayout嵌套使用weight属性,状况会更加严重。

因此,合理的选择布局控件重中之重

技巧四:merge标签的使用

merge主要作用是作为一个复用层级的root标签。它能够去除一个包裹嵌套层级的View Group,我们在轻量化布局代码时,尝尝会抽离出许多layout布局,而将这些子布局嵌套到根布局时,这个连接的节点,通过使用Merge能够去除不必要的根标签,降低层级。

  • 应用场景和要求:merge主要和include配合,当父布局和include加载子布局标签一样时使用。通常建议在FrameLayout和LinearLayout中使用,复杂布局可能会造成维护时更加复杂

  • 代码:

      ------------------根布局------------------	
    
      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">
      	<TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="Merge标签Head TextView" />		
          <include
              layout="@layout/layout_merge"></include>
      </LinearLayout>
    
      ------------------layout_merge------------------
    
      <?xml version="1.0" encoding="utf-8"?>
      <merge xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          >
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="Merge 1标签Content" />
      
          <include layout="@layout/layout_merge_inner"></include>
      </merge>
    
      ------------------layout_merge_inner 再次嵌套merge------------------
      
      <?xml version="1.0" encoding="utf-8"?>
      <merge xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">
      
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="Merge 1标签Foot" />
      </merge>
    
  • 使用注意点:

    • merge只是作为一个root标签使用,他是个虚拟的View,因此不能通过findviewbyid实例化
    • merge可以重复嵌套
    • merge再次声明的父布局约束参数不生效

二、不见不画,待见再画


不见不画——就是在不可见的界面,我们如果不用去绘制,那么可以想象一下,展现在我们眼睛里的,看起来复杂重叠的UI就是一层假象的薄膜。有点类似网上看的,新闻主播看似西装革履的播报新闻,其实桌子下面凉飕飕的大裤衩子,哈哈哈。

待见再画——我把它归纳为一直延迟加载的技巧,我们的App是动态的,UI也是,不同时刻不同操作都会造成UI的不断变化。一次性加载所有布局那是傻瓜,循序渐进才是王道。要做到延迟加载,我们需要理解《布局被Inflate时做了哪些操作?》,由我这篇文章可以知道,View在xml布局时不管声明的GONE还是VISIBLE都会被加载并且占用资源。因此,想要延迟加载一个子View,得有别的办法,如下:


技巧五:动态代码加载

通过动态代码加载,也可以实现布局的延迟加载

//使用LayoutInflater来加载activity_main.xml视图
FrameLayout rootView = (FrameLayout) LayoutInflater.from(this).inflate(R.layout.activity_main, null);
setContentView(rootView);

View childView = LayoutInflater.from(this).inflate(R.layout.layout_set_foucs_cx, rootView);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) childView.getLayoutParams();
layoutParams.gravity = Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL ;
childView.setLayoutParams(layoutParams);

技巧六:ViewStub标签的使用(延迟加载)

ViewStub可以理解为占位替代标签,它是个超级轻量级View,加载的过程对资源的占用非常小,是实现布局延迟加载利器

  • 使用:

      在布局中声明:
       <ViewStub
          android:id="@+id/vstb_view"
          android:layout_width="@dimen/setcamera_cs_h_land"
          android:layout_height="@dimen/setcamera_cs_h_land"
          android:layout_gravity="bottom|center_horizontal"
          android:layout_marginBottom="@dimen/pr_bottom_height"
          android:layout="@layout/layout_lazay"
          android:orientation="vertical"
          />
      需要被加载ViewStub包裹的布局时:
      通过:
      	ViewStub stub = (ViewStub) findViewById(R.id.vstb_awb_cs);
          stub.inflate();
      	或者
      	stub.setVisibility(View.VISIBLE);
    
  • 注意点:ViewStub只是作为占位框使用,当我们延迟加载完布局时,ViewStub就被摒弃了。

技巧七:clipRect & quickReject

这个技巧主要用于我们自定义View中如何去避免overdraw

在Android 4.4以后,系统有自动检测overdraw的功能,会通过避免绘制那些完全不可见的组件来尽量减少 Overdraw,那些Nav Drawer里面不可见的View就不会被执行浪费资源。但是但我们在自定义View或者布局中重写了onDraw方法后,系统无法监控并自动优化
了。因此,我们需要自己去指定给系统知道,哪些部分需要绘制,哪些部分是overdraw的区域。

  • canvas.clipRect():可以理解为指定画布的区域,区域以外的绘制效果都不会生效
  • canvas.quickReject():判断是否和某个矩形相交

总结

如何避免overdraw,有一些感想,实际优化有很多的技巧,大部分只有熟记心中,时刻避免和检测即可。但要想实现快速开发扁平化的优秀App,基础功底要扎实,需要我们深入理解内部布局的源码实现,从而自然而然的去做出选择,比如基本布局的绘制原理,只有理解了才会得出最优解!

猜你喜欢

转载自blog.csdn.net/sslinp/article/details/85121066
今日推荐