阿里一、二、三次技术面都被问到Android布局优化,全面复盘一波优化思路和优化方案

3. 具体优化方案


3.1 删除布局中无用的控件和层级

3.2 选择耗费性能较少的布局

如果布局中即可使用LinearLayout也可以使用RelativeLayout,那就采用LinearLayout。因为RelativeLayout在绘制时需要对子View分别进行了竖直和水平方向的两次测量,而Linearlayout在绘制时是根据我们设置的方向分别调用不同的测量方法。注意一点如果LinearLayout中子View使用了layout_weight属性时同样需要对子View进行两次测量以确定最终大小(对此不了解的小伙伴们可以查看源码中onMeasureonLayout方法本文就不多贴源码)。LinearLayoutFrameLayout都是一种性能耗费低的布局。但是很多时候单纯通过一个LinearLayoutFrameLayout无法实现产品的效果,需要通过嵌套的方式来完成。这种情况下建议使用RelativeLayout,因为嵌套就相当于增加了布局的层级,同样会降低程序的性能。

评论多次提到Constraintlayout,由于笔者用的较少忘了说,疏忽了,疏忽了[手动哭笑]。面对复杂度高的布局(比RelativeLayoutLinearLayout多次嵌套)Constraintlayout确实更简单,绘制时间更短。但面对复杂度较低的布局,RelativeLayoutConstraintLayoutonMesaure阶段快数倍。下图为Hierarchy Viewer的测试结果(里面一个TextView,一个ImageView,关于Hierarchy Viewer的使用会在下文布局调优工具中):

  • 性能耗费低的布局 = 功能简单 = FrameLayout、LinearLayout

  • 性能耗费高的布局 = 功能复杂 = RelativeLayout(ConstraintLayout)

  • 嵌套所耗费的性能 > 单个布局本身耗费的性能

3.3 提高布局的复用性(使用 布局标签)

使用 标签提取布局间的公共部分,通过提高布局的复用性从而减少测量 & 绘制时间

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout

xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:background=“@color/colorAccent”>

<ImageView

android:layout_width=“wrap_content”

android:layout_height=“match_parent”

android:layout_alignParentLeft=“true”

android:paddingLeft=“15dp”

android:src=“@mipmap/ic_titilebar_back”/>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_centerInParent=“true”

android:text=“@string/title”

android:textColor=“@color/white”

android:textSize=“18sp”/>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“match_parent”

android:layout_alignParentRight=“true”

android:gravity=“center”

android:padding=“15dp”

android:text=“@string/more”

android:textColor=“@color/white”

android:textSize=“16sp”/>

<?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=“match_parent”

android:layout_height=“match_parent”

android:gravity=“center”

android:text=“Hello World!” />

标签只支持以android:layout_开头的属性(android:id除外),需要注意一点如果使用了android:layout__这种属性,那么要求android:layout_width 和android:layout_height必须存在,否则其他android:layout__形式的属性无法生效,下面是一个指定了android:layout_*属性的示例

<include

android:id=“@+id/include_title”

android:layout_width=“match_parent”

android:layout_height=“48dp”

layout=“@layout/include_title”/>

3.4 减少布局的层级(使用 布局标签)

布局标签一般和 标签一起使用从而减少布局的层级。例如当前布局是一个竖直方向的LinearLayout,这个时候如果被包含的布局也采用了竖直方向的LinearLayout,那么显然被包含的布局文件中的LinearLayout是多余的,这时通过 布局标签就可以去掉多余的那一层LinearLayout。如下所示:

<?xml version="1.0" encoding="utf-8"?>

<Button

android:id=“@+id/button”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“Button” />

<Button

android:id=“@+id/button2”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“Button” />

<?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”>

3.5 减少初次测量 & 绘制时间

3.5.1使用 标签

ViewStub继承了View,它非常轻量级且宽和高都为0,因此它本身不参与任何的绘制过程,避免资源的浪费,减少渲染时间,在需要的时候才加载View。因此ViewStub的意义在于按需求加载所需的布局,在实际开发中,很多布局在正常情况下不会显示,比如加载数据暂无数据,网络异常等界面,这个时候就没必要在整个界面初始化的时候将其加载进来,通过ViewStub就可以做到在使用时在加载,提高了程序初始化时的性能。如下一个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”>

<ImageView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:background=“@mipmap/ic_empty_order”/>

<TextView

android:layout_below=“@+id/iv_empty”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“暂无数据”/>

<?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”>

//view_stub是ViewStub的id,empty是empty_data.xml这个布局根元素的id

<ViewStub

android:id=“@+id/view_stub”

android:inflatedId=“@+id/empty”

android:layout=“@layout/empty_data”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

/>

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);

//加载ViewStub中的布局的两种方式setVisibility或inflate

mViewStub.setVisibility(View.VISIBLE);

mViewStub.inflate();

}

使用ViewStub需注意:当ViewStub通过setVisibility或inflate方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub就不再是布局结构中的的一部分。目前ViewStub中的layout还不支持使用 标签。

3.5.2尽可能少用布局属性 wrap_content

布局属性 wrap_content 会增加布局测量时计算成本,应尽可能少用

3.6 减少控件的使用(善用控件属性)

在绘制布局中,某些情况下我们可以省去部分控件的使用。下文介绍几种常见的情况:

3.6.1 TextView文字加图片

上图布局,通常想到的是一个相对布局里包含一个TextView和两个ImageView。事实上我们只需要一个TextView就可以实现

<TextView

android:layout_width=“match_parent”

android:layout_height=“50dp”

android:layout_marginTop=“20dp”

android:paddingLeft=“16dp”

android:paddingRight=“16dp”

android:gravity=“center_vertical”

android:drawableLeft=“@mipmap/icon_my_unlock” // 设置左边显示的icon

android:drawablePadding=“10dp” // 设置icon和文本的间距

android:drawableRight=“@mipmap/icon_right” // 设置右边显示的icon

android:text=“@string/account_unlock”

/>

3.6.2 LinearLayout分割线

上图布局,通常是用高为1dp的View来显示,实际上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:divider=“@drawable/divider_line”

android:dividerPadding=“16dp”

android:showDividers=“middle”

android:orientation=“vertical”>

<TextView

android:layout_width=“match_parent”

android:layout_height=“50dp”

android:paddingLeft=“16dp”

android:paddingRight=“16dp”

android:gravity=“center_vertical”

android:drawableLeft=“@mipmap/icon_my_unlock”

android:drawablePadding=“10dp”

android:drawableRight=“@mipmap/icon_right”

android:background=“@color/color_FFFFFF”

android:text=“@string/account_unlock”

/>

<TextView

android:layout_width=“match_parent”

android:layout_height=“50dp”

android:paddingLeft=“16dp”

android:paddingRight=“16dp”

android:gravity=“center_vertical”

android:drawableLeft=“@mipmap/icon_my_unlock”

android:drawablePadding=“10dp”

android:drawableRight=“@mipmap/icon_right”

android:background=“@color/color_FFFFFF”

android:text=“@string/account_unlock”

/>

<TextView

android:layout_width=“match_parent”

android:layout_height=“50dp”

android:paddingLeft=“16dp”

android:paddingRight=“16dp”

android:gravity=“center_vertical”

android:drawableLeft=“@mipmap/icon_my_unlock”

android:drawablePadding=“10dp”

android:drawableRight=“@mipmap/icon_right”

android:background=“@color/color_FFFFFF”

android:text=“@string/account_unlock”

/>

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android=“http://schemas.android.com/apk/res/android”

android:shape=“rectangle”>

核心代码就是对LinearLayout设置divider

  • divider 设置分割线样式,需注意不能简单只给个颜色值,比如#f00或者@color/xxx这样,drawable一定要是个有长、宽概念的drawable,当然你也可以直接一张图片当divider。

  • dividerPadding 设置分割线两边的间距

  • showDividers 设置分割线显示位置。其中middle控件之间显示;beginning第一个控件上面显示分割线;end最后一个控件下面显示分割线,none不显示分割线

3.6.3 TextView的行间距和占位符的使用

<TextView

android:id=“@+id/text”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:lineSpacingExtra=“10dp”

android:text=“@string/test_text”

android:textSize=“20sp”

android:textColor=“@color/colorPrimary”/>

通过lineSpacingExtra设置行间隔,test_text内容为:

标题:%1KaTeX parse error: Undefined control sequence: \n at position 2: s\̲n̲时间:%2s\n内容:%3$s

在MainActivity中的使用

mText.setText(String.format(getResources().getString(R.string.test_text), “测试”, “2018-8-9”,“测试测试”));

占位符的使用方法:%n表示第n位要被替换的,

d表示整型占位符,$f表示浮点型占位符

4. 布局调优工具


在实际开发中哪怕注意了上述的优化方案,难免还是会出现布局性能的问题。这时我们可使用布局调优工具来分析问题。本文将介绍常用的几种工具。

4.1 Lint

Lint 是Android Studio 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量问题,同时提供一些解决方案,而且这个过程不需要我们手写测试用例。 Lint 的使用路径: 工具栏 -> Analyze -> Inspect Code

默认是检查整个项目,我们可以点击 Custom scope 自定义检查范围

  • Project Files:所有项目文件

  • Project Production Files:项目的代码文件

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

默认是检查整个项目,我们可以点击 Custom scope 自定义检查范围

  • Project Files:所有项目文件

  • Project Production Files:项目的代码文件

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

[外链图片转存中…(img-3A2OjpBg-1715422807393)]

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

[外链图片转存中…(img-mHBskMin-1715422807394)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值