Android 性能优化 - UI [进阶]
本文主要总结和记录 Android
开发过程中对 UI
绘制上的优化,优化 UI
绘制可以减少绘制的时间,尽可能快速的将界面展示出来,还可以减轻 CPU
的压力,避免过度绘制,保证 UI
的流畅度。
前言
界面 UI
每隔 16 ms 请求绘制一次,相当于 1000ms/16
=> 60fps
,60fps
是人能感觉到的最高帧率,也就是说超过 60fps
是没有必要的,同时,如果低于 30fps
将会无法流畅展示内容。
过度绘制
- 调试过度绘制
通过手机的开发者选项可以调试过度绘制,设置 -> 开发者选项 -> 调试GPU过度绘制 -> 显示GPU过度绘制
打开调试过度绘制以后,界面会显示不同的颜色,分别代表过度绘制的次数:
颜色 | 描述 | 表示 |
---|---|---|
蓝紫色 | overdraw 1倍 | 绘制了2次,大片的蓝紫色是可以接受的 |
绿色 | overdraw 2倍 | 绘制了3次,中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们 |
淡红 | overdraw 3倍 | 绘制了4次,小范围可以接受 |
深红 | overdraw 4倍 | 绘制了5+次,这是错误的,要修复 |
查看微信的过度绘制情况,首页基本在2倍以内,其他页面列表有3倍的情况,4倍的绘制一般出现在极小的区域内,比如文字和图标等。
目标是尽量增加蓝紫色的区域,减少红色区域。
- GPU 使用
通过手机的开发者选项可以开启 GPU
呈现模式分析, 设置 -> 开发者选项 -> GPU呈现模式分析 ,可以查看某个界面的 GPU
占用情况。
UI 层级
如果布局层级嵌套过深,也会导致 UI
绘制的问题,尤其是使用 xml
布局文件,因为一方面解析 xml
就需要耗费大量的 CPU
,另外布局 measure
的时候,子布局需要告知父布局自己的大小和占据的位置,层级过多之后就会占用更多的时间和内存。
Hierarchy Viewer
是一个查看 UI
布局层级的工具,使用 AndroidStudio
在 Tools -> Android -> Device Monitor
不过真机无法调试,只能使用模拟器,不过大家可以参考这个项目 GitHub-ViewServer,不过在电脑上面查看会很卡,而且拖动起来很不方便。
如果项目中使用了网络框架,可以使用:
1 | compile 'com.facebook.stetho:stetho:1.4.2', |
然后在 chrome://inspect
查看布局,点击指定布局,手机会同时高亮显示
UI 优化解决办法
针对以上两种情况,解决 UI
绘制问题主要是 减少过度绘制 和 减少布局层级 两个方面。
- 删除重复无用的背景
如果层级覆盖的情况下,优先设置底层 View
的背景,顶层如果具有相同颜色的背景,就不要重复设置啦。
如果底层的 View
覆盖了整个屏幕,那么 Window
的背景也是不需要的,可以使用:
1 | getWindow().setBackgroundDrawable(null); |
- 合理选择布局
布局要遵循 增加宽度,减小深度 的原则,尽可能的减少 UI
层级
不使用绝对布局 AbsoluteLayout
。
复杂布局,优先使用 RelativeLayout
,可以更好的控制子控件的位置。
相同的层级情况下,优先使用 LinearLayout
,他的布局效率更高。
尽可能少用 layout_weight
属性,他会造成多次测量。
使用 ConstraintLayout
,可以很好的减少布局的层级。
- Xml Drawable
规则图形,尽量使用 shape.xml
代替图片
使用 selector
时,将 normal
状态下的颜色设置为 transparent
- Include
使用 <include/>
标签复用布局,这个其实并不能起到优化 UI
绘制的作用,不过讲合适的布局分离处理做成独立的 xml
,可以更好的将布局组件化。
1 | <include |
使用 <include/>
需要注意几点,在 <include/>
标签中可以使用部分属性来从新定义布局,如 width
、height
、visibility
、margin
、id
等,如指定了 id
,将会覆盖 <include/>
里面根布局的 id
,造成查找不到的情况。
- ViewStub
如果一个控件大多数情况下不进行显示,那么使用 ViewStub
要比使用 Visibility
更好,他不会占据任何位置,解析 xml
也不会耗费更多资源。
1 | <ViewStub |
当需要展示内部布局时,使用 inflate()
展开布局,布局展开后,ViewStub
里面的内容就会代替 ViewStub
的位置,同时 ViewStub
就查找不到了,而且我们也不希望展开多次,所以要做一下空判断,不为空时,使用 setVisibility
展示布局。
1 | private View viewStubView; |
- Merge
使用 xml
布局时,最外层只能有一个父控件,当我们希望把其中部分提取到 <include/>
中时,就需要在子布局最外层再加一层控件包含,这样就增加了布局的深度。
<merge/>
就是为了解决上述问题,它主要是用来将两个相同的布局合并为一个,主要用于两种情况:
第一种情况是,调用 setContentView()
时,其实是将我们的布局设置到了 id
为 R.id.content
的 FrameLayout
中,那么如果我们的顶层布局也是 FrameLayout
,同时没有 background
和 padding
等属性,那么就可以使用 <merge/>
,将二者合为一个,理解起来也简单,本来就是两个一样的布局套在一起,合为一个并不影响。
第二种情况是,使用 <include/>
时也是一样的道理,如果 <include/>
中的顶层布局和他将要添加到的布局里面的 layout
是一样的,那么就可以使用 <merge/>
,节省一层布局,如下:
1 | // test_layout.xml |
由于 <include/>
顶层 View
也是 LinearLayout
,因此可以使用 <merge/>
代替
1 | <merge |
目前维护的几个项目,求 ✨✨✨✨
- SocialSdk 登录分享功能原生接入
- LightAdapter 轻量级适配器
- ImageEditor 图片处理,裁剪旋转,贴纸涂鸦,滤镜等
- WeexCube Weex 容器方案
- Kotlin 学习系列总结,共计 22 篇
- 本文链接: http://cdevlab.top/article/d1ceed0f/
- 版权声明: 版权所有,转载请注明出处!