不断学习,做更好的自己!💪
视频号 | ||
欢迎打开微信,关注我的视频号:程序员朵朵 | | |
前言
什么是性能
快(流畅的体验)
稳(稳定)
省(省电/流量)
小(安装包小)
这四点很形象的代表了性能的四个方面,同时也让我们知道我们 App 现在是否是款性能良好的 APP,如果有一项不达标,那么说明我们的应用有待优化。
过度绘制
定义
过度绘制(Overdraw):指的是屏幕上的某个像素在同一帧的时间内被绘制了多次。
检测
Android手机上面的开发者选项提供了工具来检测过度绘制,可以按如下步骤来打开:
开发者选项->调试GPU过度绘制->显示过度绘制区域
注意:
有些过度绘制是无法避免的。因此在优化界面时,应该尽量让大部分的界面显示为真彩色(即无过度绘制)或者为蓝色(仅有 1 次过度绘制)。尽量避免出现粉色或者红色。
优化
1. 移除布局中不需要的背景
- 移除 Window 默认的 Background
方式一:在theme中设置
<style name="AppTheme" parent="主题">
<item name="android:windowBackground">@null</item>
</style>
方式二:在Activity的onCreate()方法中添加:
getWindow().setBackgroundDrawable(null);
- 移除控件中不需要的背景
2. 将layout层级扁平化
- Layout Inspector来查看layout的层次结构。
在Android Studio中点击 Tools > Android > Layout Inspector。然后在出现的 Choose Process 对话框中,选择想要检查的应用进程即可。
Layout Inspector会自动捕获快照,然后会显示以下内容:
- View Tree:视图在布局中的层次结构。
- Screenshot:每个视图可视边界的设备屏幕截图。
- Properties Table:选定视图的布局属性。
- 使用嵌套少的布局
我们可以使用 LinearLayout 和 RelativeLayout 来完成。但是 LinearLayout 相比于 RelativeLayout,就多了一层,所以 RelativeLayout 明显是一个更优的选择。 - 使用 include 标签能够复用布局
可以将一个指定的布局文件加载到当前的布局文件中。如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/title_bar"/>
<TextView
android:id="@+id/first_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rect_color_primary"
android:gravity="center"
android:padding="@dimen/item_spacing"
android:text="FirstExample"
android:textColor="@color/colorPrimaryDark"/>
...
</LinearLayout>
需要注意的是,如果include标签指定了layout_*这种属性,那么要求
android:layout_width和android:layout_height必须存在,否则其他的android:layout_*形式的属性无法生效。
- 使用 merge 标签减少嵌套
一般和include标签一起使用从而减少布局的层级。在上述示例中,由于当前布局是一个竖直方向的LinearLayout,用过merge标签就可以去掉多余的那一层LinearLayout。如下:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button2"/>
</merge>
ViewStub
ViewStub继承了View,它非常轻量级且宽和高都是0,因此它本身不参与任何的布局和绘制过程。比如:网络异常时的界面,这个时候就没有必要在整个界面初始化将其加进来,通过ViewStub就可以做到在使用的时候再加载,提高了程序初始化的性能:
<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/panel_import"
android:layout="@layout/layout_empty_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"/>
panel_import是@layout/layout_empty_txt这个布局的根元素的id。
在需要加载ViewStub中的布局时,可以按照如下两种方式:
(ViewStub)findViewById(R.id.stub_import).setVisibility(View.VISIBLE);
或者
View mImportPanel = ((ViewStub)findViewById(R.id.stub_import)).inflate();
通过上述两个方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub就不再是整个布局结构中的一部分了。
- 使用lint来优化布局的层次结构
lint是一个静态代码分析工具,可以用来协助优化布局的性能。要使用lint,点击Analyze> Inspect Code即可。
布局性能方面的信息位于Android> Lint> Performance下,我们可以点开它来看下一些优化建议。
下面是lint的一些优化技巧:
- 使用复合图片
如果一个线性布局中包含一个 ImageView 和一个 TextView,可以使用复合图片来替换掉 - 合并根节点
如果一个FrameLayout 是整个布局的根节点,并且也没有提供背景、留白等等,那么可以使用标签来替换掉,因为DecorView本身就是一个FrameLayout。 - 移除布局中无用的叶子
布局是一个树形的结构,如果一个布局没有子 View 或者背景,那么可以把它移除掉(这布局本身就不可见了)。 - 移除无用的父布局
如果一个布局没有兄弟,也不是ScrollView 或者根 View,并且也没有背景,那么可以把这个父布局移除掉,然后把它的子view移到它的父布局下。 - 避免过深的层次结构
过多的布局嵌套不利于性能,可以使用更扁平化的布局,如RelativeLayout、GridLayout、ConstraintLayout等布局来提高性能。布局默认的最大深度为10。
布局优化技巧
- 如果父控件有颜色,也是自己需要的颜色,那么就不必在子控件加背景颜色
- 如果每个自控件的颜色不太一样,而且可以完全覆盖父控件,那么就不需要再父控件上加背景颜色
- 尽量减少不必要的嵌套
- 能用
LinearLayout
和FrameLayout
,就不要用RelativeLayout
,因为RelativeLayout
控件相对比较复杂,测绘也想要耗时。 - 使用
include
和merge
增加复用,减少层级 -
ViewStub
按需加载,更加轻便 - 复杂界面可选择
ConstraintLayout
,可有效减少层级