Android性能优化
1 内存优化1.1 内存泄漏Java虚拟机中的对象内存泄漏1.2 内存溢出(OOM Out Of Memory)内存泄漏一般导致应用卡顿,极端情况会导致OOM,OOM的原因是因为超过内存的阈值。原因主要有两方面:内存泄漏,导致无法及时释放导致OOM;一些逻辑消耗了大量内存,无法及时释放或者超过导致OOM;**能够消耗大量内存的,绝大多数是因为图片加载。这是OOM出现最频繁的地方。图片加载,一个是控
1 内存优化
1.1 内存泄漏
1.2 内存溢出(OOM Out Of Memory)
内存泄漏一般导致应用卡顿,极端情况会导致 OOM,OOM 的原因是因为超过内存的阈值。原因主要有两方面:
- 内存泄漏,导致无法及时释放导致 OOM;
- 一些逻辑消耗了大量内存,无法及时释放或者超过导致 OOM;
能够消耗大量内存的,绝大多数是因为图片加载。这是 OOM 出现最频繁的地方。图片加载,一个是控制每次加载的数量,二是保证每次滑动的时候不进行加载,滑动完进行加载。一般情况使用先进后出,而不是先进先出。 不过一般我们图片加载都是使用 fresco 或者 Glide 等开源库。
下面是导致内存溢出的两种情况:
通过命令行查看内存消耗情况(adb shell dumpsys meminfo 包名 -d):
2 UI 优化
对于大多数的手机的屏幕的刷新频率是 60hz,也就是如果在 1000/60=16.67ms 内没有把这一帧的任务执行完毕,就会发生丢帧现象,丢帧是造成界面卡顿的直接原因。
渲染操作通常依赖于两个核心组件:CPU 和 GPU。CPU 负责包括 measure、layout 等计算操作,GPU 负责 Rasterization(珊格化操作),珊格化就是将矢量图转换为位图的过程。
位图:也称为点阵图,是由像素点组成的图像,每个像素都包含了颜色和位置信息。放大位图时,可以看到构成整体图像的无数单个像素点。
矢量图:面对对象的图形或者绘制图像,矢量文件中的图形元素称为对象。每个对象都是一个自成一体的实体,具有颜色、形状、大小和屏幕位置等属性。
手机页面上按照一个个的像素来显示的,比如将一个 Button 拆分成一个个的像素显示到手机屏幕上。
UI 渲染的目的就是减轻 CPU、GPU 的压力,除去不必要的操作,保证每帧能在 16ms 内处理完所有的的计算、绘制、渲染等操作,使 UI 能够顺滑、流畅的显示出来。
UI 优化主要包括布局优化以及 View 的绘制优化。有时候我们打开某个软件,会出现卡顿的情况,这就是 UI 的问题。什么情况会导致卡顿呢?一般是如下几种情况:
- 在 UI 线程中做耗时操作,导致 UI 线程卡顿;
- 布局 Layout 过于复杂,无法在 16ms 内完成渲染;
- 同一时间动画执行的次数过多,导致 CPU 或 GPU 负载过重;
- View 过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使 CPU 或 GPU 负载过重;
- View 频繁的触发 measure、layout,导致 measure、layout 累计耗时过多及整个 View 频繁的重新渲染;
- 内存频繁触发 GC 过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;
- 冗余资源及逻辑等导致加载和执行缓慢;
- ANR;
可以看见,上面这些导致卡顿的原因都是我们平时开发中非常常见的。有些运行起来还可以的程序,一旦进行一些瞬时测试或压力测试,就会发现很多性能问题。
2.1 布局优化
2.1.1 GPU 绘制
对于 UI 性能的优化还可以通过开发者选项中的 GPU 过度绘制工具来进行分析。 在设置 -> 开发者选项 -> 调试 GPU 过度绘制(不同设备可能位置或者叫法不同)中打开调试后可以看见如下图(对 settings 当前界面过度绘制进行分析):
以下说明:
蓝色(1x 过度绘制),淡绿(2x 过度绘制),淡红(3x 过度绘制),深红(4x 过度绘制)代表了 4 种不同程度的 Overdraw 情况,我们的目标就是尽量减少红色 Overdraw,看到更多的蓝色区域。
UI 渲染优化的第一步就是找到 Overdraw(过度绘制),即屏幕上的某个像素在同一帧的时间内被绘制了多次,从而增加了 CPU、GPU 的压力。
Overdraw 有时候是因为 UI 布局存在大量重叠的部分,还有的时候是因为非必须的重叠背景。例如某个 Activity 有一个背景,然后里面的 Layout 又有自己的背景,同时子 View 又分别有自己的背景。仅仅是通过移除非必须的背景图片,这就能够减少大量的红色 Overdraw 区域,增加蓝色区域的占比。这一措施能够显著提升程序性能。
如果布局中既能采用 RealtiveLayout 和 LinearLayout,那么直接使用 LinearLayout,因为 Relativelayout 的布局比较复杂,绘制的时候需要花费更多的 CPU 时间。如果需要多个 LinearLayout 或者 Framelayout 嵌套,那么可采用 Relativelayout。因为多层嵌套导致布局的绘制有大部分是重复的,这会减少程序的性能。
2.1.2 GPU 呈现模式分析
设置–>开发者选项–>GPU呈现模式分析–>在屏幕上显示为条形图,如图所示:
随着界面的刷新,界面上会以实时柱状图来显示每帧的渲染时间,柱状图越高表示渲染时间越长,每个柱状图偏上都有一根代表 16ms 基准的绿色横线,每一条竖着的柱状线都包含三部分(蓝色代表测量绘制 Display List 的时间,红色代表 OpenGL 渲染 Display List 所需要的时间,黄色代表 CPU 等待 GPU 处理的时间),只要我们每一帧的总时间低于基准线就不会发生 UI 卡顿问题(个别超出基准线其实也不算什么问题的)。
2.2 绘制优化
绘制优化主要是指 View.onDraw 方法需要避免执行大量的操作:
- onDraw 方法不需要创建新的局部对象,这是因为 onDraw 方法是实时执行的,产生大量的临时对象,导致占用了更多内存,并且使系统不断的 GC,降低了执行效率;
- onDraw 方法不需要执行耗时操作,在 onDraw 方法里少使用循环,因为循环会占用 CPU 的时间。导致绘制不流畅,卡顿等等。 Google官方指出,View 的绘制帧率稳定在 60dps,这要求每帧的绘制时间不超过 16ms(1000/60)。虽然很难保证,但我们需要尽可能的降低;
60dps 是目前最合适的图像显示速度,也是绝大部分Android 设备设置的调试频率,如果在 16ms 内顺利完成界面刷新操作可以展示出流畅的画面,而由于任何原因导致接收到 VSYNC 信号的时候无法完成本次刷新操作,就会产生掉帧的现象,刷新帧率自然也就跟着下降(假定刷新帧率由正常的 60fps 降到 30fps,用户就会明显感知到卡顿)。
3 速度优化
3.1 ANR 问题(application not responding)
Android 官方规定:Activity 如果在 5s 内无响应事件(屏幕触摸事件或者键盘输入事件);BroadcastReceiver 如果在 10s 内无法处理完成;Service 如果 20s 内无法处理完成。这三种情况会导致 ANR。
上面说的三种导致 ANR 的情况,绝大多数就是因为线程阻塞导致的。 那么应该如何处理呢?Android 系统为我们提供了若干组工具类来解决此问题:
- Asynctask:为 UI 线程与工作线程之间进行快速处理的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的场景;
- HandlerThread:为某些回调方法或者等待某些执行任务的执行设置一个专属的线程,并提供线程任务的调度机制;
- ThreadPool:把任务分解成不同的单元,分发到各个不同的线程上,并发处理;
- IntentService:适合执行由 UI 触发的后台任务。并可以把这些任务执行的情况通过一定的机制反馈给 UI;
3.2 图片处理
- 使用 WebP 格式:同样的照片,采用 WebP 格式可大幅节省流量,相对于 JPG 格式的图片,流量能节省将近 25% 到 35 %;相对于 PNG 格式的图片,流量可以节省将近 80%。最重要的是使用 WebP 之后图片质量也没有改变;
- 使用缩略图;
3.3 网络请求处理
- 对服务端返回数据进行缓存,设定有效时间,在有效时间之内不走网络请求,减少流量消耗;
- 在某些情况,尽量少使用 GPS 定位,如果条件允许,尽可能使用网络定位;
- 下载、上传,尽可能使用断点续传;
4 启动优化
Android 应用的启动方式分为三种:冷启动、暖启动、热启动,不同的启动方式决定了应用 UI 对用户可见所需要花费的时间长短,冷启动消耗的时间最长。 基于冷启动方式的优化工作也是最考验产品用户体验的地方。
4.1 冷启动
应用启动时,后台没有该应用的进程,这样的启动就是冷启动。例如,第一次启动 APP,又或者说杀死进程后第一次启动。那么对比其他两种方式。冷启动自然是耗时最久的。
应用发生冷启动时,系统一定会执行下面的三个任务:
- 开始加载并启动应用;
- 应用启动后,显示一个空白的启动窗口(启动闪屏页);
- 创建应用信息;
那么创建应用信息,系统就需要做:
- 创建 Application
- 启动主线程
- 创建应用入口的 Activity
- 填充加载布局 View
- 执行 View 的绘制过程(measure —> layout —> draw)
这其中有两个 creation 工作,分别为 Application 和 Activity creation。它们均在 View 绘制展示之前。所以,在应用自定义的 Application 类和第一个 Activity 类中,onCreate() 方法做的事情越多,冷启动消耗的时间越长。
4.2 暖启动
当应用中的 Activities 被销毁,但在内存中常驻时,应用的启动方式就会变为暖启动。相比冷启动,暖启动过程减少了对象初始化、布局加载等工作,启动时间更短。但启动时,系统依然会展示闪屏页,直到第一个 Activity 的内容呈现为止。
4.3 热启动
相比暖启动,热启动时应用做的工作更少,启动时间更短。热启动产生的场景很多,比如:用户使用返回键退出应用,然后马上又重新启动应用。
4.4 如何优化
先对比一下下三种启动的时间:
可以看到三者的明显的差距。
为了提升用户体验,可以把闪屏页当作一个 Fragment 嵌套在 MainActivity 中,这样就可以在进入闪屏时直接预加载主页的 View。闪屏页结束后直接 remove 掉就可以了。
对于启动的优化就是减少耗时操作,总结如下:
- 主线程中涉及到 Shareperference 能否在非 UI 线程执行;
- Application 的创建过程中尽量少的进行耗时操作;
- 减少布局的层次,并且生命周期回调的方法中尽量减少耗时的操作;
或者使用指令:adb shell am start -S -W [packageName]/[packageName.MainActivity],-S 是重新启动应用,也可以查看启动时间:
5 电量优化
音乐和视频是耗电量最大的。对于电量优化:
- 需要进行网络请求时,我们需先判断网络当前的状态;
- 在多网络请求的情况下,最好进行批量处理,尽量避免频繁的间隔网络请求;
- 在同时有 wifi 和移动数据的情况下,我们应该直接屏蔽移动数据的网络请求,只有当 wifi 断开时在调用,因为,wifi 请求的耗电量远比移动数据的耗电量低的低;
- 后台任务要尽可能少的唤醒 CPU,比如说,锁屏时,QQ 的消息提示行就是唤醒了 CPU,但是它的提示只有在你打开锁屏或者进行充电时才会进行提示;
参考
https://zhuanlan.zhihu.com/p/434867334
https://www.kancloud.cn/kancloud/android-performance/53233
Android App启动时间测量
更多推荐
所有评论(0)