布局优化三两事儿

前言:

相信喜欢玩游戏的小伙伴儿们都知道一个指标FPS(frame per second),FPS是每秒钟填充图像的帧数,而一般情况下只要达到了60FPS的画质渲染,人眼识别来说已经具备了较高的平滑度了。而我们的Android也是遵循这一设计来渲染画面的,即每过16.67毫秒重绘一次页面布局。那么为什么有时候我们还能感受到显著的卡顿呢。一般情况下页面渲染是在UI线程中执行的,一旦页面过于复杂,重绘时间过长或者UI线程正在执行其他任务,就有可能导致重绘时间超过了16.67毫秒,这是我们所观察到的画面就不够平滑了,也就是我们常说的卡顿,这是一种极不好的用户体验。而良好的页面布局是避免卡顿的重要因素之一,本篇文章就来总结下Android布局优化方案。

 

布局合理性检测:

使用设备GPU呈现模式检测

Android 包含一些设备上的开发者选项,可帮助您直观地查看您的应用可能会在何处遇到界面渲染问题,如执行不必要的渲染工作,或执行长时间的线程和 GPU 操作。

在开始使用前需确定设备的android 版本为4.1(API 16)或更高,并且开启了开发者模式。

第一步:在设备上找到 设置 -> [根据不同的设备,中间目录可能不同]  -> 开发者模式(开发人员选项)

第二步:在开发人员选项中找到GPU呈现模式分析

第三步:点击GPU呈现模式分析,在弹出的对话框中选择在屏幕上显示为条形图,即在设备的屏幕上叠加条形图显示;

第四步:打开你所需要监控的应用以监控和检测布局层次的合理性。

下面我们来说明下这些条形图都分别代表什么:

竖条区段 渲染阶段 说明
交换缓冲区 表示 CPU 等待 GPU 完成其工作的时间。如果此竖条升高,表示应用在 GPU 上执行太多工作。
命令问题 表示 Android 的 2D 渲染程序向 OpenGL 发出绘制和重新绘制显示列表的命令所花的时间。此竖条的高度与执行每个显示列表所花的时间的总和成正比。显示列表越多,红色竖条就越高。
同步和上传 表示将位图信息上传到 GPU 所花的时间。大区段表示应用花费大量的时间加载大量图形。
绘制 表示用于创建和更新视图显示列表的时间。如果竖条的此部分很高,表明可能有许多自定义视图绘制,或 onDraw 方法执行的工作很多。
测量/布局 表示在视图层次结构中 onLayout 和 onMeasure 回调上所花的时间。大区段表示处理视图层次结构需要很长时间。
动画 表示评估运行该帧的所有动画程序所花的时间。如果此区段很大,表示您的应用可能在使用性能欠佳的自定义动画程序,或因更新属性而导致一些意料之外的工作。
输入处理 表示应用执行输入事件回调中的代码所花的时间。如果此区段很大,表示应用花太多时间处理用户输入。不妨考虑将此类处理任务分流到其他线程。
其他时间/VSync 延迟 表示应用执行两个连续帧之间的操作所花的时间。它可能表示界面线程中进行的处理太多,而这些处理任务本可以分流到其他线程。

 

使用Layout Inspector调试布局

Android Studio还提供了Layout Inspector来使得应用程序视图层次结构得到直观的表示。在Android Studio 2.x 中我们经常使用Hierarchy Viewer来分析应用程序的布局层级,然而它在Android Studio 3.X 中已经被移除了,而代替它的即是Layout Inspector。你可以通过它将应用布局与设计模型进行比较、显示应用的放大视图以及在运行时检查应用布局的细节。

使用步骤如下:

第一步:在连接的设备或者模拟器上运行你的应用程序;

第二步:在Android Studio 找到并依次点击 Tools -> Layout Inspector;

第三步:在弹出的对话框中选择你的应用进程,点击OK

显示内容如下:

左侧View Tree部分:左侧部分是布局中视图的层次结构。

中间屏幕截图部分:中间部分为设备上显示的应用程序的屏幕快照,其中还显示了每个视图的布局边界线。

右侧Properties Table部分:右侧部分是选定视图的布局属性。

 

使用GPU过度绘制调试布局

在android设备的开发者模式中还提供了另一个功能--调试GPU过度绘制,当你的应用在同一帧中多次绘制相同的像素点时,便会发生过度绘制现象。在同一帧中多次绘制相同的像素点人类肉眼也只会感知到最后一次绘制的颜色,因此前面的多次绘制都是无效且无意义的,只会消耗性能,而使用调试GPU过度绘制可以很直观的看到哪些像素区块产生了过度绘制及重复程度。下面介绍两种使用方式:

第一种方式:使用时首先打开开发者模式,找到调试GPU过度绘制并打开,在弹出的对话框中选择“显示过度绘制区块”,这时设备屏幕上就会显示绘制情况颜色,最后我们打开需要调试的应用程序界面查看即可。

第二种方式:通过adb命令来开启调试GPU过度绘制

    //打开调试GPU过度绘制
    adb shell setprop debug.hwui.overdraw show

    //关闭调式GPU过度绘制
    adb shell setprop debug.hwui.overdraw false

通过以上任意一种方式开启调试GPU过度绘制之后即可看到下图这种过度绘制显示界面。 

  •        真彩色:没有过度绘制
  •    蓝   色:过度绘制 1 次
  •    绿   色:过度绘制 2 次
  •    粉   色:过度绘制 3 次
  •    红   色:过度绘制 4 次或更多次

布局优化:

删除无用的布局层级

这个应该很好理解,对于不必要的布局层级,我们应当能删即删。对于较为复杂的布局,应该劲量减少View的嵌套层级,Google的官方建议是不多于10层。对于能够使用LinearLayout和RelativeLayout实现的布局应当优先使用LinearLayout,因为RelativeLayout布局计算和绘制所占用CPU的时间较长。

 

避免过度绘制,移除不必要的背景

关于过度绘制问题,我们可以通过上面所介绍的设备上的调节GPU过度绘制检测和发现界面上过度绘制的部分,然后做针对性的处理。一般而言,我们应该删除那些没有显示在屏幕上的背景色,比如当子布局完全覆盖了上层布局,而且又同时设置了背景色,那么这个时候就会产生过度绘制,因为上层布局被完全覆盖,我们认为其背景色是无意义的,因为它不会被显示,这个时候就可以不设置上层布局的背景色从而避免过度绘制。

 

使用<include>标签、<marge>标签、<ViewStub>标签

<include>标签 实际上并不能使我们布局绘制的性能得到提升,但它对布局优化也起到了相当大的作用,它能够使我们重用Layout布局,对于那些经常被重复使用的布局,可以使用<include>标签来引用它,从而避免了重复工作。需要注意的是,<include>标签只支持android:layout_开头的属性(id属性除外);如果包含的布局文件的根元素也指定了id属性,则以<include>指定的为准;android: lay-out_width 和 android:layout_height 必须存在,否则其他 android:layout_* 形式的属性无法生效。

    <include layout="@layout/activity_add_phone"
             android:layout_height="match_parent"
             android:layout_width="match_parent"/>

 

<merge>标签 主要用来进行UI布局的优化的,删除多余的层级,优化UI。需要注意的是,<merge/>仅仅能作为XML布局的根标签使用。当Inflate以<merge/>开头的布局文件时,必须指定一个父ViewGroup而且必须设置。

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <Button  
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"  
        android:text="@string/add"/>  
    <Button  
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"  
        android:text="@string/delete"/>  
</merge>

 

<ViewStub>标签 的继承自View,宽高都为0,相当于一个占位符,可以做到按需加载。意思就是在绘制界面是<ViewStub>是不被绘制的,也不占用CPU的时间,当我们需要加载是再通过inflate()方法加载,当调用inflate()函数的时候,ViewStub被引用的资源替代,并且返回引用的view。 这样程序可以直接得到引用的view而不用再次调用函数findViewById()来查找了。

View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

采用ConstaintLayout布局

约束布局ConstraintLayout 是一个ViewGroup,可以在Api9以上的Android系统使用它,它的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整小部件。从 Android Studio 2.3 起,官方的模板默认使用 ConstraintLayout。

在开发过程中经常能遇到一些复杂的UI,可能会出现布局嵌套过多的问题,嵌套得越多,设备绘制视图所需的时间和计算功耗也就越多。这时候我们就需要考虑使用约束布局ConstraintLayout,因为ConstraintLayout使用起来比RelativeLayout更灵活,性能更出色!还有一点就是ConstraintLayout可以按照比例约束控件位置和尺寸,能够更好地适配屏幕大小不同的机型。

如果对ConstraintLayout约束布局还不怎么了解的可以参考ConstraintLayout官方文档

 

onDraw方法优化

众所周知onDraw方法是View的绘制方法,它会被频繁的调用,因此,我们应该避免在onDraw方法中创建局部对象和做耗时操作。

猜你喜欢

转载自blog.csdn.net/ledding/article/details/107663835
今日推荐