300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 大厂架构师经验分享!Android-性能优化最佳实践

大厂架构师经验分享!Android-性能优化最佳实践

时间:2022-07-21 07:48:13

相关推荐

大厂架构师经验分享!Android-性能优化最佳实践

本文由玉刚说写作平台提供写作赞助

原作者:Mr.S

版权声明:本文版权归微信公众号玉刚说所有,未经许可,不得以任何形式转载

原文链接:https://juejin.im/post/5b50b017f265da0f7b2f649c

什么是性能

快,稳,省,小,这四点很形象的代表了性能的四个方面,同时也让我们知道我们App现在是否是款性能良好的APP,如果有一项不达标,那么说明我们的应用有待优化。

很多时候我们注重功能实现,保证能用,但是我们会发现,这样的应用很难拿的出手,里面的槽点太多了,性能很差,但是又不知道从哪里下手进行优化,那么我们就一步一步来,看看我们到底应该怎么优化我们的APP。

1 、布局优化

和UI相关的首先就是布局,特别是在开发一些复杂界面的时候,通常我们都是采用布局嵌套的方法,每个人的布局思路不太一样,写出的也不太一样,,所以就可能造成嵌套的层级过多。

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

白话显示一个布局就好比我们盖一个房子,首先我们要测量房子的大小,还要测量房间里面各个家具的大小,和位置,然后进行摆放同时也要对房子进行装修,如果我们是一层,都在明面上,干起活来敞亮也轻松,可是有的人的房子,喜欢各种隔断,分成一个一个的大隔断间,每个大隔断间里还有小隔断间,小隔断间里有小小隔断间,还有小小小隔断间。。。N层隔断间。

看到这些头皮发麻吧,而且是一个大隔断间里面所有的小隔断,小小隔断等等都测量完摆放好,才能换另外一个大隔断,天呢,太浪费时间了,不能都直接都放外面吗?也好摆放啊,这么搞我怎么摆,每个隔断间都要装修一遍,太浪费时间了啊。

我们的Android虚拟机也会这么抱怨,咱们家本来就不富裕,什么都要省着用,你这么搞,肯定运转有问题啊,那么多嵌套的小隔断间需要处理,都会占用cpu计算的时间和GPU渲染的时间。显示GPU过度绘制,分层如下如所示:

通过颜色我们可以知道我们应用是否有多余层次的绘制,如果一路飘红,那么我们就要相应的处理了。

所以我们有了第一个优化版本:

优化 1.0

如果父控件有颜色,也是自己需要的颜色,那么就不必在子控件加背景颜色如果每个自控件的颜色不太一样,而且可以完全覆盖父控件,那么就不需要再父控件上加背景颜色尽量减少不必要的嵌套能用LinearLayout和FrameLayout,就不要用RelativeLayout,因为RelativeLayout控件相对比较复杂,测绘也想要耗时。

做到了以上4点只能说恭喜你,入门级优化已经实现了。

针对嵌套布局,谷歌也是陆续出了一些新的方案。对就是includemergeViewStub三兄弟。

include可以提高布局的复用性,大大方便我们的开发,有人说这个没有减少布局的嵌套吧,对,include确实没有,但是include和merge联手搭配,效果那是杠杠滴。

merge的布局取决于父控件是哪个布局,使用merge相当于减少了自身的一层布局,直接采用父include的布局,当然直接在父布局里面使用意义不大,所以会和include配合使用,既增加了布局的复用性,用减少了一层布局嵌套。

ViewStub它可以按需加载,什么意思?用到他的时候喊他一下,再来加载,不需要的时候像空气一样,在一边静静的呆着,不吃你的米,也不花你家的钱。等需要的时候ViewStub中的布局才加载到内存,多节俭持家啊。对于一些进度条,提示信息等等八百年才用一次的功能,使用ViewStub是极其合适的。这就是不用不知道,一用戒不了。

我们开始进化我们的优化

优化 1.1

使用include和merge增加复用,减少层级ViewStub按需加载,更加轻便

可能又有人说了:背景复用了,嵌套已经很精简了,再精简就实现了不了复杂视图了,可是还是一路飘红,这个怎么办?面对这个问题谷歌给了我们一个新的布局ConstraintLayout

ConstraintLayout可以有效地解决布局嵌套过多的问题。ConstraintLayout使用约束的方式来指定各个控件的位置和关系的,它有点类似于 RelativeLayout,但远比RelativeLayout要更强大(照抄隔壁IOS的约束布局)。所以简单布局简单处理,复杂布局ConstraintLayout很好使,提升性能从布局做起。

再次进化:

优化 1.2

复杂界面可选择ConstraintLayout,可有效减少层级

2、绘制优化

我们把布局优化了,但是和布局息息相关的还有绘制,这是直接影响显示的两个根本因素。

其实布局优化了对于性能提升影响不算很大,但是是我们最容易下手,最直接接触的优化,所以不管能提升多少,哪怕只有百分之一的提升,我们也要做,因为影响性能的地方太多了,每个部分都提升一点,我们应用就可以提升很多了。

我们平时感觉的卡顿问题最主要的原因之一是因为渲染性能,因为越来越复杂的界面交互,其中可能添加了动画,或者图片等等。我们希望创造出越来越炫的交互界面,同时也希望他可以流畅显示,但是往往卡顿就发生在这里。

这个是Android的渲染机制造成的,Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,但是渲染未必成功,如果成功了那么代表一切顺利,但是失败了可能就要延误时间,或者直接跳过去,给人视觉上的表现,就是要么卡了一会,要么跳帧。

View的绘制频率保证60fps是最佳的,这就要求每帧绘制时间不超过16ms(16ms = 1000/60),虽然程序很难保证16ms这个时间,但是尽量降低onDraw方法中的复杂度总是切实有效的。

这个正常情况下,每隔16ms draw()一下,很整齐,很流畅,很完美。

往往会发生如下图的情况,有个便秘的家伙霸占着,一帧画面拉的时间那么长,这一下可不就卡顿了嘛。把后面的时间给占用了,后面只能延后,或者直接略过了。

既然问题找到了,那么我们肯定要有相应的解决办法,根本做法是 减轻onDraw()的负担。

所以

第一点:onDraw方法中不要做耗时的任务,也不做过多的循环操作,特别是嵌套循环,虽然每次循环耗时很小,但是大量的循环势必霸占CPU的时间片,从而造成View的绘制过程不流畅。

第二点:除了循环之外,onDraw()中不要创建新的局部对象,因为onDraw()方法一般都会频繁大量调用,就意味着会产生大量的零时对象,不进占用过的内存,而且会导致系统更加频繁的GC,大大降低程序的执行速度和效率。

其实这两点在android的UI线程中都适用。

升级进化:

优化2.0

onDraw中不要创建新的局部对象onDraw方法中不要做耗时的任务

其实从渲染优化里我们也牵扯出了另一个优化,那就是内存优化。

3、内存优化

内存泄漏指的是那些程序不再使用的对象无法被GC识别,这样就导致这个对象一直留在内存当中,占用了没来就不多的内存空间。

内存泄漏是一个缓慢积累的过程,一点一点的给你,温水煮青蛙一般,我们往往很难直观的看到,只能最后内存不够用了,程序奔溃了,才知道里面有大量的泄漏,但是到底是那些地方?估计是狼烟遍地,千疮百孔,都不知道如何下手。怎么办?最让人难受的是内存泄漏情况那么多,记不住,理解也不容易,关键是老会忘记。怎么办呢?老这么下去也不是事,总不能面试的时候突击,做项目的时候不知所措吧。所以一定要记住了解GC原理,这样才可以更准确的理解内存泄漏的场景和原因。不懂GC原理的可以先看一下这个JVM初探:内存分配、GC原理与垃圾收集器

本来GC的诞生是为了让java程序员更加轻松(这一点隔壁C++痛苦的一匹),java虚拟机会自动帮助我们回收那些不再需要的内存空间。通过引用计数法,可达性分析法等等方法,确认该对象是否没有引用,是否可以被回收。

有人会说真么强悍的功能看起来无懈可击啊,对,理论上可以达到消除内存泄漏,但是很多人不按常理出牌啊,往往很多时候,有的对象还保持着引用,但逻辑上已经不会再用到。就是这一类对象,游走于GC法律的边缘,我没用了,但是你又不知道我没用了,就是这么赖着不走,空耗内存。

因为有内存泄漏,所以内存被占用越来越多,那么GC会更容易被触发,GC会越来越频发,但是当GC的时候所有的线程都是暂停状态的,需要处理的对象数量越多耗时越长,所以这也会造成卡顿。

那么什么情况下会出现这样的对象呢? 基本可以分为以下四大类: 1、集合类泄漏 2、单例/静态变量造成的内存泄漏 3、匿名内部类/非静态内部类 4、资源未关闭造成的内存泄漏

1、集合类泄漏

集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄露。

举个栗子:

static List<Object> mList = new ArrayList<>();for (int i = 0; i < 100; i++) {Object obj = new Object();mList.add(obj);obj = null;}

当mList没用的时候,我们如果不做处理的话,这就是典型的占着茅坑不拉屎,mList内部持有者众多集合元素的对象,不泄露天理难容啊。解决这个问题也超级简单。把mList清理掉,然后把它的引用也给释放掉。

mList.clear();mList = null;

2、单例/静态变量造成的内存泄漏

单例模式具有其 静态特性,它的生命周期 等于应用程序的生命周期,正是因为这一点,往往很容易造成内存泄漏。 先来一个小栗子:

public class SingleInstance {private static SingleInstance mInstance;privat

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。