Android性能优化系列之布局优化

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 111

  • 112

  • 113

  • 114

  • 115

  • 116

  • 117

  • 118

  • 119

  • 120

  • 121

  • 122

  • 123

  • 124

  • 125

  • 126

  • 127

  • 128

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 111

  • 112

  • 113

  • 114

  • 115

  • 116

  • 117

  • 118

  • 119

  • 120

  • 121

  • 122

  • 123

  • 124

  • 125

  • 126

  • 127

  • 128

整个过程就是根据不同的标签解析不同的元素,首先会解析include元素,然后再解析被include进来的布局的root view元素。在我们的例子中对应的root view就是id为my_foot_parent_id的RelativeLayout,然后再解析root view下面的所有元素,这个过程是从上面注释的2~4的过程,然后是设置布局参数。我们注意看注释5处,这里就解释了为什么include标签和被引入的布局的根元素都设置了id的情况下,通过被引入的根元素的id来查找子控件会找不到的情况。我们看到,注释5处的会判断include标签的id如果不是View.NO_ID的话会把该id设置给被引入的布局根元素的id,即此时在我们的例子中被引入的id为my_foot_parent_id的根元素RelativeLayout的id被设置成了include标签中的id,即RelativeLayout的id被动态修改成了”my_foot_ly”。因此此时我们再通过“my_foot_parent_id”这个id来查找根元素就会找不到了!

所以结论就是: 如果include中设置了id,那么就通过include的id来查找被include布局根元素的View;如果include中没有设置Id, 而被include的布局的根元素设置了id,那么通过该根元素的id来查找该view即可。拿到根元素后查找其子控件都是一样的。

(2)<viewstub>标签

viewstub标签同include标签一样可以用来引入一个外部布局,不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。

viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。

下面以在一个布局main.xml中加入网络错误时的提示页面network_error.xml为例。main.mxl代码如下:

<?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" > …… <ViewStub android:id="@+id/network_error_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/network_error" /> </RelativeLayout>

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

其中network_error.xml为只有在网络错误时才需要显示的布局,默认不会被解析,示例代码如下:

<?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" > <Button android:id="@+id/network_setting" android:layout_width="@dimen/dp_160" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:text="@string/network_setting" /> <Button android:id="@+id/network_refresh" android:layout_width="@dimen/dp_160" android:layout_height="wrap_content" android:layout_below="@+id/network_setting" android:layout_centerHorizontal="true" android:layout_marginTop="@dimen/dp_10" android:text="@string/network_refresh" /> </RelativeLayout>

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

Java中通过(ViewStub)findViewById(id)找到ViewStub,通过stub.inflate()展开ViewStub,然后得到子View,如下:

private View networkErrorView; private void showNetError() { // not repeated infalte if (networkErrorView != null) { networkErrorView.setVisibility(View.VISIBLE); return; } ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout); if(stub !=null){ networkErrorView = stub.inflate(); Button networkSetting = (Button)networkErrorView.findViewById(R.id.network_setting); Button refresh = (Button)findViewById(R.id.network_refresh); } } private void showNormal() { if (networkErrorView != null) { networkErrorView.setVisibility(View.GONE); } }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

在上面showNetError()中展开了ViewStub,同时我们对networkErrorView进行了保存,这样下次不用继续inflate。这就是后面第三部分提到的减少不必要的infalte。

注意这里我对ViewStub的实例进行了一个非空判断,这是因为ViewStub在XML中定义的id只在一开始有效,一旦ViewStub中指定的布局加载之后,这个id也就失败了,那么此时findViewById()得到的值也会是空

viewstub标签大部分属性同include标签类似。

上面展开ViewStub部分代码

ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout); networkErrorView = stub.inflate();

  • 1

  • 2

  • 3

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

也可以写成下面的形式

View viewStub = findViewById(R.id.network_error_layout); viewStub.setVisibility(View.VISIBLE); // ViewStub被展开后的布局所替换 networkErrorView = findViewById(R.id.network_error_layout); // 获取展开后的布局

  • 1

  • 2

  • 3

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

注意:

View 的可见性设置为 gone 后,在inflate 时,这个View 及其子View依然会被解析的。使用ViewStub就能避免解析其中指定的布局文件,从而节省布局文件的解析时间,及内存的占用。另外需要提醒大家一点,ViewStub所加载的布局是不可以使用<merge>标签的

(3)<merge>标签

在使用了include后可能导致布局嵌套过多,多余不必要的layout节点,从而导致解析变慢,不必要的节点和嵌套可通过hierarchy viewer(下面布局调优工具中有具体介绍)或设置->开发者选项->显示布局边界查看。

merge标签可用于两种典型情况:

a. 布局顶结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容试图的parent view就是个FrameLayout,所以可以用merge消除只剩一个。

b. 某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。

以(1) 标签的示例为例,用hierarchy viewer查看main.xml布局如下图:

这里写图片描述

可以发现多了一层没必要的RelativeLayout,将foot.xml中RelativeLayout改为merge,如下:

<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_above="@+id/text"/> <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_alignParentBottom="true" android:text="@string/app_name" /> </merge>

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

运行后再次用hierarchy viewer查看main.xml布局如下图:

这里写图片描述

这样就不会有多余的RelativeLayout节点了。

去除不必要的嵌套和View节点


(1) 首次不需要使用的节点设置为GONE或使用viewstub

(2) 使用RelativeLayout代替LinearLayout

大约在Android4.0之前,新建工程的默认main.xml中顶节点是LinearLayout,而在之后已经改为RelativeLayout,因为RelativeLayout性能更优,且可以简单实现LinearLayout嵌套才能实现的布局。

4.0及以上Android版本可通过设置->开发者选项->显示布局边界打开页面布局显示,看看是否有不必要的节点和嵌套。4.0以下版本可通过hierarchy viewer查看。

减少不必要的infalte


(1)对于inflate的布局可以直接缓存,用全部变量代替局部变量,避免下次需再次inflate

如上面ViewStub示例中的

if (networkErrorView != null) { networkErrorView.setVisibility(View.VISIBLE); return; }

  • 1

  • 2

  • 3

  • 4

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

布局调优工具


(1) hierarchy viewer

hierarchy viewer可以方便的查看Activity的布局,各个View的属性、measure、layout、draw的时间,如果耗时较多会用红色标记,否则显示绿色。

Hierarchy Viewer是随Android SDK发布的工具,位于Android SDK/tools/hierarchyviewer.bat (Windows操作系统,mac上显示的为hierarchyviewer),使用起来也是超级简单,通过此工具可以详细的理解当前界面的控件布局以及某个控件的属性(name、id、height等)。

1)连接设备真机或者模拟器

2)启动你要观察的应用。

3)打开Hierarchyviewer,点击hierarchyviewer文件即可。

这里写图片描述

4)双击最上面的,如下图的<Focused Window>,这个是当前窗口,加载完毕后会显示当前界面层次结构。

这里写图片描述

5)观察层次结构图,这个图有点大,可以拖动。View Hierarchy窗口显示了Activity的所有View对象,选中某个View还可以查看View的具体信息,最好选择工具中的Show Extras选项。

这里写图片描述

这里写图片描述

View Hierarcy 同时能帮助你识别渲染性能比较低的部分。View节点中带有红色或黄色的点代表速度较慢的View对象。如单步运行应用程序那样,你可以这样来判断某个View 速度一直很慢,还是只在某个特定环境下速度才慢。

请注意,低性能并不表示一定有问题,特别像是ViewGroup对象,View的子节点越多,结构越复杂,性能越差。

View Hierarchy 窗口还可以帮助你找到性能问题。只要看每个View节点的性能指标(颜色点)就可以,你可以看到测量(布局或绘制)最慢的View对象是哪个,这样你就能快速确定,要优先察看哪个问题。

(2)Lint

先来段developer的官方引用:

Android Studio provides a code scanning tool called Lint that can help you to easily identify and correct problems with the structural quality of your code, without having to execute the app or write any test cases.

  • 1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

该图诠释了Lint工具是如何检测应用源代码的:

这里写图片描述

Android Lint是Google提供给Android开发者的静态代码检查工具。使用Lint对Android工程代码进行扫描和检查,可以发现代码潜在的问题,提醒程序员及早修正。

Android Lint使用

Lint简要来说,有以下的作用:

布局性能(以前是 layoutopt工具,可以解决无用布局、嵌套太多、布局太多)

未使用到资源

不一致的数组大小

国际化问题(硬编码)

图标的问题(重复的图标,错误的大小)

可用性问题(如不指定的文本字段的输入型)

manifest文件的错误

内存泄露 — 如:handle的不当使用 。

占内存的资源及时回收 — 如:cursor未关闭等

Analyze”菜单中选择“Inspect Code”,其中可以选择scope,即检测范围,也可以选择不同的检测配置,我们先进行默认的配置检测吧。检测需要一定的时间,结果会在EventLog显示:

这里写图片描述

可以看到,我们的项目有很多问题,比如我选择了Correctness中的Using dp instead of sp for text sizes属性,发现应用中有2处在textSize中误用了dp,其给出了类的具体位置和解决方案。可能你觉得这些无关大雅,那么可以查看Probable bugs项,在其中找到一项 String comparison using ‘==’,instead of ‘equals()’,可以看到SecurityBankCardListActivity类中的有一行代码:

this.mBankCard.getCardId() == mBankCard.getCardId()//cardId为String类型

  • 1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

在此就不一一列举。

可能你会觉得Lint分析的太过详细,我无法迅速找到问题,那么你可以点击这里写图片描述,其分为四类,我们应只关注前2类。

这里写图片描述

AS的Lint配置

打开设置对话框,找到Editor,然后是Inspections,选择某一个Lint选项,修改严重等级,如图:

这里写图片描述

最后贴一下Lint检查的常见类型:

最后贴一下Lint检查的常见类型:

1.Correctness:Messeges

(1)字符串国际化不完全

(2)国际化的字符串,在默认位置(default locale),没有定义

2.Correctness

(1)Xml中view的id重名

(2)代码中使用的某些API高于Manifest中的Min SDK

(3)字符串国际化中,同一名字的的String-Array对应的item值不相同 (4)Activity没有注册到Manifest

(5)使用已经废弃的api

(6)避免使用px,使用dp

(7)添加不需要的权限

3.Performance

(1) 避免在绘制或者解析布局(draw/layout)时,分配对象。eg,Ondraw()中实例化Paint().

(2)Layout中无用的参数。

(3)可优化的布局:如一个线性布局(一个Imageview和一个TextView),可被TextView和一个Compound Drawable代替。

(4)可优化的代码:如SparseArray可代替一个Interger2Object的Hashmap

(5)优化layout,比如如果子view都是wrap_content,则设置android:baselineAligned为false,则When set to false, prevents the layout from aligning its children’s baselines.

(6)使用FloatMath代替Math,执行sin()和ceil(),以避免float的两次转换。

(7)Nested weight (内外均有weight)将拖累执行效果

(8)未被使用的资源

(9)Overdraw 即指定theme的activity会自己绘制背景,但是布局中会再一次设置背景

(10)View或view的父亲没有用

4.Security

(1)设置setJavascriptEnable将导致脚本攻击漏洞(XSS vulnerabilities)

5.Usability:Icons

(1) 图片尺寸在转换成不同dpi时,存在不能整除的问题,比如2*24px

(2)显示有些base 和browser的资源名不同,但图片内容完全相同。

6.Usability

(1)自定义view缺少默认的构造方法

7.Usability:Typography

(1)特殊字符需用编码代替,如“_”需要用“–”

8.Accessibility

(1)ImageView缺少src内容

检查Overdraw


Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源。

手机原本为了保持视觉的流畅度,其屏幕刷新频率是60hz,即在1000/60=16.67ms内更新一帧。如果没有完成任务,就会发生掉帧的现象,也就是我们所说的卡顿。

这里写图片描述

debug GPU overdraw

在Android系统内部也有一个神器可以查看app的UI的过度绘制情况,在开发者选项中有个debug GPU overdraw(调试GPU过度绘制),打开之后有off(关闭),show overdraw areas(显示过度绘制区域),show areas for Deuteranomaly(为红绿症患者显示过度绘制区域)

这里写图片描述

我们选择show overdraw areas,发现整个手机界面的颜色变了,在打开过度绘制选项后,其中的蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。

这里写图片描述

Profile GPU rendering

其次android系统还内置了Profile GPU rendering工具,这个工具也是在开发者选项中打开,它能够以柱状图的方式显示当前界面的渲染时间

这里写图片描述

蓝色代表测量绘制的时间,或者说它代表需要多长时间去创建和更新你的DisplayList.在Android中,一个视图在可以实际的进行渲染之前,它必须被转换成GPU所熟悉的格式,简单来说就是几条绘图命令,复杂点的可能是你的自定义的View嵌入了自定义的Path. 一旦完成,结果会作为一个DisplayList对象被系统送入缓存,蓝色就是记录了需要花费多长时间在屏幕上更新视图(说白了就是执行每一个View的onDraw方法,创建或者更新每一个View的Display List对象).

橙色部分表示的是处理时间,或者说是CPU告诉GPU渲染一帧的地方,这是一个阻塞调用,因为CPU会一直等待GPU发出接到命令的回复,如果柱状图很高,那就意味着你给GPU太多的工作,太多的负责视图需要OpenGL命令去绘制和处理.

红色代表执行的时间,这部分是Android进行2D渲染 Display List的时间,为了绘制到屏幕上,Android需要使用OpenGl ES的API接口来绘制Display List.这些API有效地将数据发送到GPU,最总在屏幕上显示出来.

下面我们通过一个小demo来实践一下

刚打开这个项目,我们就发现了在第一个有过度绘制问题,效果如下

这里写图片描述

存在问题

在按钮overdraw上面就有个红色的过度绘制区域

在文本框This is test的布局中也是红色过度绘制区域

解决方法

要解决这个问题,我们首先需要分析这是怎么引起的。分析到activity_main.xml的布局文件时,发现这里使用了多个嵌套的LinearLayout布局,而且每个LinearLayout都会使用一次android:background设置一次自己的背景颜色,他们造成了过度绘制。

仔细分析在其中一个嵌套ImageView的LinearLayout布局背景颜色与最外层的背景颜色是一样的,属于不需要的背景色,因此将这个LinearLayout中的android:background属性删除,这时发现文本框布局已经不再是红色了

这里写图片描述

咋看之下一切都很完美,但其实整个ui其实还含有一个隐含的绘制效果,那边是在activity中,使用setContentView(R.layout.activity_main)设置布局的时候,android会自动填充一个默认的背景,而在这个UI中,我们使用了填充整个app的背景,因此不需要默认背景,取消也很简单,只需要在activity中的onCreate方法中添加这么一句就行了

现在看最终优化效果

这里写图片描述

OVERDRAWVIEW页面的问题

在overdrawviewactivity中只有一个自定义的图案,而这个自定义的图案引起了过度绘制的问题

这里写图片描述

解决方法

首先这个也是填充了整个ui界面的绘制图片,因此我们也在activity中的onCreate方法中添加getWindow().setBackgroundDrawable(null);取消默认绘制。

继续研究,发现过度绘制问题是由于OverDrawView类中的ondraw方法中多次绘制了矩形导致的,代码如下:

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); mPaint.setColor(Color.GRAY); canvas.drawRect(0, 0, width, height, mPaint); mPaint.setColor(Color.CYAN); canvas.drawRect(0, height/4, width, height, mPaint); mPaint.setColor(Color.DKGRAY); canvas.drawRect(0, height/3, width, height, mPaint); mPaint.setColor(Color.LTGRAY); canvas.drawRect(0, height/2, width, height, mPaint); }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

通过分析得知,颜色为GRAY的矩形的高度其实不需要设置为整个屏幕的高度,它的高度只需要设置为它所显示范围的高度就可以了,因此可以设为height/4。

其他的矩形也是同样的道理,因此更改这里的代码为:

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); mPaint.setColor(Color.GRAY); canvas.drawRect(0, 0, width, height/4, mPaint); mPaint.setColor(Color.CYAN); canvas.drawRect(0, height/4, width, height/3, mPaint); mPaint.setColor(Color.DKGRAY); canvas.drawRect(0, height/3, width, height/2, mPaint); mPaint.setColor(Color.LTGRAY); canvas.drawRect(0, height/2, width, height, mPaint); }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

优化的界面

这里写图片描述

至此,布局优化的内容就到此结束了,有不足的地方,欢迎大家评论指出

3

0

我的同类文章

性能优化_(5)_

http://blog.csdn.net

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

![
[]


文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

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

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

[外链图片转存中…(img-0BbtgIlW-1714739673023)]

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

优化的界面

这里写图片描述

至此,布局优化的内容就到此结束了,有不足的地方,欢迎大家评论指出

3

0

我的同类文章

性能优化_(5)_

http://blog.csdn.net

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

[外链图片转存中…(img-E59nfZ84-1714739673024)]

[外链图片转存中…(img-YfX0a9zZ-1714739673025)]
[]

[外链图片转存中…(img-wpFXYeiz-1714739673025)]
[外链图片转存中…(img-d7VzmNrK-1714739673026)]

文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

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

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值