国民级App性能优化方案+深层次实践=让你真正进阶的资料

518 阅读11分钟

如何吃透Android性能优化技能&黑科技,使你的App达到极致的流畅与稳定,今天我们主要详解掌握性能优化难点及工具使用。


1、性能优化分析工具学习

在开始代码优化之前,先得学会使用性能分析工具。以下三个工具都是谷歌官方推出的,可以帮助我们定位分析问题,从而优化我们的APP。

· System Trace

Systrace是一个收集和检测时间信息的工具,它能显示CPU和时间被消耗在哪儿了,每个进程和线程都在其CPU时间片内做了什么事儿.而且会指示哪个地方出了问题,以及给出Fix建议。给出的结果trace文件是以html形式打开的,直接用浏览器打开查看十分方便。打开方法:打开DDMS后,连接手机,点击手机上方一排按钮中的SysTrace按钮。打开的效果如下图:


在代码中打点方式如下:


· Hierarchy Viewer

Hierarchy Viewer提供了一个可视化的界面来观测布局的层级,让我们可以优化布局层级

,删除多余的不必要的View层级,提升布局速度。另外,开发者模式中调试GPU过度绘制选项也可以进行视图层级调试。在SDK-> tools目录下打开hierarchyviewer.bat即可。效果如下图:


· TraceView

一个图形化的工具,用来展示和分析方法的执行时间。也是一款性能优化的神器。可以通过像打
log一样的方式去定位代码的执行时间,从而可以准确定位是哪一段代码的执行消耗了太多时间。相比SysTrace,功能更强大,使用起来也更复杂。


2、布局优化

Android中布局优化主要包含以下三个方面:布局层级和测量次数、布局过度绘制、绘制过程

1、布局层级与测量次数

布局层级越多,绘制耗时就会相应增加。考虑使用布局层级比较少的方案.

(一)合理选择父容器

在布局层数相同时,我们优先选择测量次数较少的父容器通常我们选取的优先级为:

FrameLayout、不带Layou_wight的LinearLayuut、RelativeLayout。因为带有Layot_weight的LinearLayout和RelativeLayout会测量两次。

总结来看,首先优先布局层级少的方案,在布局层级相同时,采用测量次数少的。

那么如何分析布局层级呢?

(1)Android Device Monitor

Android studio3.0开始,Google不建议使用它, 因此我们需要手动在sdk目录下的tools中找到它,之后运行你的apk并且选择Hierarchy View,就可以查看对应的层级关系,如下:

(2)Component Tree

在Android studio.3.0之后,我们可以使用Component Tree,它同样提供了查看组件层级的功能,具体如下:


选择并查看我们的xm布局,点击左下角的design,则可以看到左侧层级关系。

(二)标签

除了上面提到的方式外,我们还可以通过使用标签来减少层级和复用组件

(1)include标签

include标签的作用就是可以直接引用已有的布局,而不需要重新写布局。而通常会将include和merge标签相结合使用,下面会介绍merge标签。

例如:

这里include引用的是一个Linear Layout的布局,虽然我们不需要重复写这个布局,但是却增加了层级关系。

(2)merge标签

merge标签通常是作为include标签的辅助扩展,就是为了解决引入include后导致布局层级增加问题,使用merge标签后,引入的布局中的View就会作为父布局的子View。如下:



使用include标签引入后,层级关系如下:


现在我们把标签改为merge,层级关系如下:


可以明显看到中间少了一层。

(3)ViewStub标签

ViewStub继承于View,它是一个轻量级且宽高为0的组件,本身不参与布局和绘制。因此使用它可以做到在不需要的时候不加载,在需要的时候再加载,从而提高性能。那么如何做到需要的时候显示呢?

可以通过以下两种方式:

setVisiable

(View.Visiable)或者
findViewById().inflate()

(三)使用Constaint Layout

Constaint Layout允许在不使用任何嵌套的情况下创建复杂布局,与Relative Layout

相似,可以依赖兄弟容器和父控件之间的相对关系。常见属性:

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintTop_toTopOf="parent"

app:layout_constraintLeft_toRightOf="@+id/iv_image"

例如:



2、过度绘制

过度绘制其实指的是屏幕内某个像素在同一帧的时间内被绘制了多次。

而在多层次重叠的UI结构里,如果不可见的UI也在绘制的话,就会导致某些像素区域被绘制多次,从而浪费大量的cpu和gpu资源。目前提供两种方式来检测过度绘制:

手机自带检测工具手机的开发者模式中会有一项为调试GPU过度绘制>显示GPU
过度绘制,设置后,打开任何一个app,就可以看到界面上出现蓝、绿、粉、红四种颜色中的一种或者多种。

蓝色:1次过度绘制

绿色:2次过度绘制

粉丝:3次过度绘制

红色:4次过度绘制

例如:


· Android device monotor

打开Hierarchy Viewer(/'haɪərɑːkɪ/),运行模拟器,打开对应的Activity界面,就可以看到如下:

其中每一个View中,下面三个点依次表示测量、布局、绘制的时间,红点和黄点表示速度慢,而蓝绿则相对好一些。

在了解了如何分析过度绘制后,我们如何去处理过度绘制?

(1)去掉Windwo的默认背景

一般来说我们使用的Activity都会有一些默认的主题,通常这个主题会有一个对应的背景,被
DecoreView持有,我们自定义布局时如果又添加了一个背景图或者设置背景色,就会产生一个
overdraw,因此可以考虑去掉默认的背景。我们可以在onCreate()的setContentView
之前调用getWindow().setBackGroundDrawable(null)或者是在theme
中加入windowbackground=“null”

(2)去掉其他不必要的背景

有时候我们为layout设置一个背景,而它的子View也有背景,此时就会造成重叠。针对这种情况我们首先考虑添加背景是否需要,如果需要我们可以考虑通过selector普通状态下设置背景为透明,点击状态下设置背景来减少重绘。

(3)优化onDraw方法

避免在onDraw()方法中分配对象,因为onDraw方法可能被多次调用,这样的话可能会产生很多不必要的对象使用ClipRect制定个绘制区域在使用自定义View时,我们可以利用此方法来指定一块可见区域,用于绘制,其他区域则会被忽略,即只绘制clipRect指定的区域。例如在图片层叠时,我们将重叠部分不再绘制,只绘制不重叠部分。直接绘制如下:

可以看到重叠部分出现了过度绘制,而实际上重叠部分我们不需要绘制底层部分,因为我们只能看到上面的图层,因此我们可以利用clipRect()来指定区域。如下:

(4)标签也就是前面提到的include、merge、viewStub标签

3、线程优化

1.不能通过非UI线程对View进行操作。因为Android的UI不是安全的,如果View能被不同的线程所访问或修改,那么就可能在程序的执行期间,产生不可预期的行为或者并发错误。

2.使用线程时,避免在循坏中使用同步,因为获取和释放锁的操作代价很大。会引起CPU资源的损耗。

3.处理多线程以及线程间通信时,使用HandlerThread来操作,它内部包装了Looper,记得不用的时候退出/释放资源哦。

4.当工作线程与UI线程之间通信的时候,推荐使用AsyncTask(Android 7.0后内部任务变成串行处理,不再会出现以前并行时超过任务数执行饱和策略的情况)

5. Loader可以用来代替AsyncTask的某些情况,因为Loader的生命周期是独立的(与Application Context有关),当Activity/Fragment销毁重建时,它仍然在,而且它特别使用异步操作,比如AsyncTaskLoader代替AsyncTask也可以实现后者的功能,但是生命周期完全独立于Activity。切记Loader使用完记得销毁。

6.当你的Service不需要交互时,请使用可以自动停止的IntentService。

7.当你希望延长BroadcastReceiver的生命周期时,例如启动一个后台线程IntentService。在onReceiver中调用BroadcastReceiver.goAsync(),它会返回一个PendingResult对象,这时,广播接收器的生命周期会延长持续到PendingResult.finish()方法调用。

8.线程池最好用构造方法手动创建,而不要用Executors来直接调用工厂方法,这样利于明白线程池的运行规则,避免用了错误的线程池导致资源耗尽。

9.给线程一个好听的名字,调试时候用。

10.线程池设置线程的存活时间,以保证空闲线程准确释放。

4、内存泄漏

内存泄漏通俗的讲是一个本该被回收的对象却因为某些原因导致其不能回收。我们都知道对象是有生命周期的,从生到死,当对象的任务完成之后,由Android系统进行垃圾回收。我们知道Android系统为某个App分配的内存是有限的(这个可能根据机型的不同而不同),当一个应用中产生的内存泄漏比较多时,就难免会导致应用所需要的内存超过这个系统分配的内存限额,最终导致OOM(OutOfMemory)使程序崩溃。

5、算法优化

关于查找的算法很多种:基本查找、二分查找、二叉树、斐波那契等,它们时间复杂度和空间复杂度不同,选择适合的算法事半功倍。比如一个顺序队列n个数据,选择基本查找的时间复杂度为O(n),而二分查找时间复杂度为O(log 2 n),显然n越大,基本查找消耗的时间越久,性能损耗越高。再比如,查询某个文件,你可以采用基本查找、也可以先排序、再进行二分查找。

6.其他优化点

异步处理耗时任务

在Activity、Fragemnt的onCreate等初始化方法中,如果执行了太耗时的操作(例如读取各种数据),会影响页面的加载速度,让用户觉得APP太慢。这时候可以异步处理这些耗时任务,减小应用启动的时候的负担。

替换矢量图

尽管矢量图有诸多优点,但矢量图的绘制是消耗性能的。在应用初始化加载等比较影响用户体验的地方,还是建议使用Bitmap来代替矢量图,提高APP开启效率。

正则表达式

经小伙伴用TraceView不断的打点发现,正则表达式非常消耗时间。因此尽管正则表达式非常优雅,涉及到性能问题的时候,可以改为其他判断方式来提高APP性能。

浮点类型

在Java中浮点类型的运算大概比整型数据慢两倍,因此整型数据能解决的问题尽量用整型。

减少冗余

log 开发的时候用于调试的log,在项目上线的时候没用的要及时删除。当然有用的log还是要留下,以便以后分析问题。

删除无用资源

没用用的资源会增大APK大小,既然没有用了,上线的时候当然要及时删除。

Lint代码检查 使用Lint等静态代码检查工具可以帮助我们发现很多隐藏的问题。Lint检查出来的问题越少,说明代码越规范,越不容易出现各种问题,APP性能自然也会提升。

滥用全局广播

全局广播也是十分消耗性能的一个点。对于应用内的通讯,使用接口回调,EventBus等手段比起广播是更好地选择。动态注册广播的时候,也不要忘了广播的注销。

7、总结

好了Android性能优化难点及工具使用就先介绍到这里了,如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。对文章中提到的掌握性能体系化建设方案、性能分析优化思路以及优化方案、掌握稳定性保障方案、掌握性能优化黑科技及常见方案改良等常见资料感兴趣的可以关注转发后评论【资料】获取。