安卓性能优化:布局优化分析与调优
Author:zhoulujun Date:
先安利一下《Android XML/iOS-StoryBoard/HTML5 排布布局分析与对比—style的异同》,接下来主要以 《『Android性能优化手册』布局分析与调优 》作为主线修改与增补
对于GUI的开发来说,因为页面布局造成页面卡顿、交互差,对于新手还是经常发生的。当页面设计复杂起来,层级越来越深,页面会变得越来越卡顿,这在web html 与css非常常见,在Android,因为页面毕竟不大,出现的概率少些,但是也得去避免。
无论是浏览器对html与css 的处理还是Android原生的页面渲染,都是以层叠来实现页面的展示,在Android中,一个Activity绑定着一个Window,Window又管理着页面的根ViewGroup,然后ViewGroup中包含着View,层层包裹,就如同Photoshop中的图层:
因此如果同一个位置上面叠加了多个层级,该像素点就会被绘制多次。冗余的渲染会浪费了大量的GPU和CPU资源,因而增加了绘制时长,从而出现卡顿现象。
而人眼与大脑之间的协作无法感知超过60fps的画面更新,也就是1秒内如果必须展示60帧,才能看起来流畅,1s=1000ms,因此,平均每16ms就要绘制一帧,如果布局层级很深,渲染时长超过16ms,就会看起来稍显卡顿。
如何分析当前页面绘制情况
debug调试GPU过度绘制-检测页面渲染层级
Android系统支持我们查看页面绘制情况的功能,在手机的设置-开发者选项中有一个调试GPU绘制的开关,打开之后,会发现手机界面上出现了很多颜色区域:
GPU过度绘制一共有以下几种颜色:
原色:没有过度绘制
蓝色:1 次过度绘制
绿色:2 次过度绘制
粉色:3 次过度绘制
红色:4 次及以上过度绘制
平常开发的界面中,应该尽可能地将过度绘制控制为 2 次(绿色)及其以下
使用Layout Inspector查看布局层级
可以在新版本的AndroidStudio中,菜单栏的Tools里面找到Layout Inspector:
然后选择所要分析的进程,比如选择你自己正在运行中的应用,然后跳转到你要分析的页面,确认之后会在项目目录下生成一个captuers文件夹,Layout Inspector会根据当前手机正在显示的页面生成一个文件存放在这个目录下:
左边:页面的层级,从最顶层的DecorView开始,其下所有当前页面存在的View都会显示在这里
中间:当前页面的预览
右边:每个View的布局属性,包括宽高、padding等等。通过Layout Inspector能清晰地看到页面的层次结构,比如说布局文件中某一处重复包裹了两层View,或者不小心在自定义ViewGroup的时候多加了一层根View,在这里都能看得出来。
布局优化
总体来说,尽量减少布局层级和复杂度!
尽量不要嵌套使用RelativeLayout.
尽量不要在嵌套的LinearLayout中都使用weight属性.
Layout的选择, 以尽量减少View树的层级为主.
去除不必要的父布局.
善用TextView的Drawable减少布局层级
如果H Viewer查看层级超过5层, 你就需要考虑优化下布局了~
下面看具体的实施
移除叠加的背景
我们注册Activity时一般都会为它设置主题,主题一般都会有默认背景 windowBackground,比如下面这种:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="windowBackground">@color/colorPrimary< /item> </style>
它会为我们的页面设置一个背景,但有些时候,布局文件里面的根布局也会设置一个背景,这个时候window的background用户完全看不到,实际上没有作用,这种情况下我们可以移除它的背景:
<item name="android:windowBackground">@null</item>
刚才是针对window的背景做了处理,同理,布局嵌套中也有可能出现这种情况,比如说一个LinearLayout里包裹了两个子View,而且这两个子View刚好占满了LinearLayout的全部空间,那LinearLayout同样就没必要设置背景了。
合理使用布局设计
我们平时都是用五大布局组合成页面的结构,相同的效果,可以用不同的ViewGroup来组合实现,但是:
RelativeLayout底层会测绘两次,而LinearLayout和FrameLayout只会绘制一次(详见《 RelativeLayout和LinearLayout及FrameLayout性能分析》)。因此性能上不如LinearLayout和FrameLayout。
RelativeLayout也有它的优点,利用它的各种相对属性可以减少我们的页面层级,所以总的来说就是:
如果能减少页面层级可以考虑采用RelativeLayout,如果是相同层级的情况下,优先考虑采用FrameLayout和LinearLayout。
在LinearLayout中,采用它的layout_weight来为子View设置显示的比例,但是layout_weight同样会触发LinearLayout测量两遍,所以慎用。
个约束布局——ConstraintLayout,它的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整小部件。它与 RelativeLayout 一样有相对的属性,但性能上比RelativeLayout更胜一筹。
采用布局标签减少布局嵌套
Android中提供了几种布局标签——merge、include、ViewStub 能够为我们减少很多不必要的嵌套。
merge与include用法
merge和include 和PHP、JSP里面的非常像,也可以理解为Vue、或者react的组件。无非就是把可复用的页面布局封装成为一个组件,然后再使用的地方复用。
include只是换了种方式包裹布局,使用merge标签可以帮我们忽略掉我们的子布局的根View,相当于直接将子布局添加到我们主布局下
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <include android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/layout_item"/> </FrameLayout>
组件模块
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:background="@color/colorPrimary"/> android:text="Common Card"/> </merge>
merge标签有很多要注意的地方:
merge标签必须使用在根布局(这也正是为何推荐搭配include使用的原因)
merge会帮我们忽略掉根View,因此根View的布局属性也全都会失效,会直接采用主布局中其父View的布局属性
merge标签本质上不是一个View,对它设置的任何布局属性都是没有意义的,并且在通过LayoutInflate.inflate()方法获取它的时候,第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点。
ViewStub的用法
ViewStub可以用来包裹布局,被包裹的布局在页面加载时是没有被加载出来的,只有调用了viewStub.inflate()或者viewStub.setVisible()时,才会被加载出来,也就是类似一种懒加载的机制,很适用于用来包裹我们的一些缺省布局,比如无网络提示、加载错误提示等等,或者一些不需要页面一启动就显示出来的View,因为这些布局不一定会展示给用户,如果全部写在layout文件里面的话,页面加载的时候无论可不可见实际上都是会加载出来的(注意区分加载和可见的概念)。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ViewStub android:id="@+id/no_net_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/layout_no_net"/> <TextView android:id="@+id/loading_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="正在加载中..."/> </FrameLayout>
ViewStub组件
<?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:gravity="center" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Check Your Internet Connection"/> </LinearLayout>
其实这里都是还是GUI组件复用的思路。
include ViewStub:重用布局.
merge:解决include或自定义组合ViewGroup导致的冗余层级问题. 例如本例中的RepoItemView的布局文件实际可以用一个<merge>标签来减少一级.
ListView优化
contentView复用
引入holder来避免重复的findViewById.
分页加载
参考文章:
『Android性能优化手册』布局分析与调优 https://www.jianshu.com/p/809f95341695
Android App优化之Layout怎么摆 https://www.jianshu.com/p/4943dae4c333
转载本站文章《安卓性能优化:布局优化分析与调优》,
请注明出处:https://www.zhoulujun.cn/html/OS/Android/AndroidDevelop/2020_0924_8614.html