Android 性能优化总结

Android 设备作为移动设备，不管是内存还是 CPU 的性能都受到了一定的限制。鉴于此，Android 不能无限制的使用内存和 CPU 资源，过多的使用内存会导致内存溢出，即 OOM。而过多的使用 CPU 资源，会导致手机变得卡顿，甚至出现程序无响应的情况，即 ANR。因此，Android 程序的性能问题变得异常突出。这也要求我们再平常的编码中注意性能优化。而优化的前提是能明确的知道那种情况会导致性能出现问题。下面就总结一下性能优化的一些方法。

1. 布局优化

优化布局的思想很简单，就是尽量减少布局文件的层级，布局层间减少，意味着绘制工作量减少。如果进行布局优化？

首先删除布局中无用的控件和层级，其次有选择的使用 ViewGroup, 比如 LinearLayout 和 RelativeLayout 都可以实现，那么就选用 LinearLayout, 因为 RelativeLayout 功能复杂，他的布局过程花费更过的 CPU 时间。另外，减少嵌套是经常用也是最直观的方式。

采用 <include> 标签， <merge> 标签和ViewStub。<include> 标签主要用于布局重用，<merge> 标签用于减少嵌套，可以配合<include> 标签一块使用或者在自定义 View 时使用。ViewStub 则提供了按需加载的功能，它非常轻量级，宽高都是 0，因此本身不参与布局和绘制，很多布局在正常情况下不会显示，在特定场景下显示，因此只需要到该显示的时候再加载就好，可以提高初始化的性能。

2. 绘制优化

绘制优化是指 View 的 onDraw 方法避免执行大量的操作，主要体现在两个方面。

首先，onDraw 不要创建新的局部对象，因为 onDraw 会频繁的调用，这样会瞬间产生大量的临时对象，这不仅占用过多的内存，而且会导致频繁的 GC，降低了程序执行的效率。

其次，onDraw 方法中不要做耗时任务，也不能执行大量的循环操作，尽管每次循环都很轻量级，但大量的循环仍然十分抢占 CPU 的时间片，这会造成 View 绘制过程不流畅。谷歌官方性能优化标准，View 的绘制帧率保持在 60fps 是最佳的，这就要求每帧的绘制时间不超过 16ms (16ms = 1000/60)。

3. 内存泄漏优化

内存泄漏对开发人员的经验和开发意识要求较高，因此也是最容易犯的错误之一。内存泄漏的优化主要有两个方面，一方面就是开发中避免写出有内存泄露的代码，另一方面是通过一些分析工具找出潜在的内存泄漏而解决。列举一些容易出现内存泄漏的编码情况：

静态变量导致内存泄漏

比如 Context, View 为静态变量，它内部持有了 Activity，所以当 Activity 关闭后仍然无法释放。

单例模式导致内存泄漏

比较明显的是同上，直接引用对象持有了 Activity，导致无法释放。另一种不太明显的是注册监听方式，只注册，而缺少取消注册的情况也会引起内存泄漏。因为单例的特点是其生命周期和 Applation 保持一致，因此会导致内存泄漏。

属性动画导致内存泄漏

属性动画是一类无限循环的动画，如果在 Activity 中播放此类动画且没有在 onDestory 中停止，那么动画就会一直播放下去，因为 Activity 会被动画 View 持有，最终 Activity 无法释放。解决方法就是及时停止动画。

至于分析内存泄漏的工具有很多，比如：

Android Studio 自带的 Profiler

可以直观的看到 CPU，内存，网络变化，也可以做很多模拟操作，但是不太容易看出内存泄漏，需要配合 MTA 使用。

MAT

MAT 全称 Eclipse Memory Analyzer，是一款强大的内存泄漏分析工具。

Android LeakCanary

Android LeakCanary 易于集成，自动检测出内存泄漏，十分好用。目前我们项目中也是用的 LeakCanary。使用方法可以去参考文档。

上面这些工具只是帮助我们分析和定位内存泄漏的位置，等知道问题出在哪了，才能去解决问题。

4. 响应速度优化

核心思想就是避免在主线程中做耗时操作，当有耗时操作时应当放在子线程中去操作。响应速度过慢一般体现在 Activity 启动速度上面，主线程做太多事情，会导致黑屏甚至出现 ANR。

耗时操作放在子线程

业务优化，比如采用新算法，代码重构，偿还历史债务等

通过分析 trace.txt 文件定位 ANR 的原因

主线程和子线程抢占同步锁，子线程持有了主线程所需的锁

5. RecyclerView 和 Bitmap 优化

避免再 onBindView 中执行耗时操作

根据列表的滑动状态来控制任务的执行频率，比如当列表快速滑动时不适合开启大量的异步任务

对于 Bitmap 主要通过 BitmapFactory.Options 来根据需要对图片进行采样，加载合适大小的图片，降低 Bitmap 的大小

6. 线程优化

线程优化主要是采用线程池，避免程序中存在大量的 Thread，线程池可以重用内部的线程，从而避免了线程的创建和销毁代码的性能开销，同时线程池还能有效的控制线程中的最大并发数，避免大量的线程因互相抢占资源而导致阻塞现象的发生。因此开发过程中尽量使用线程池，而不是每次都要创建一个 Thread。

7. 优化建议

尽量避免创建过多对象

不要过多使用枚举，枚举占用的内存空间比整型大

常量使用 static final 来修饰

使用一些 Android 特有的数据结构，他们有更好的性能

适当使用软引用，弱引用

采用内存缓存和磁盘缓存

尽量采用静态内部类，这样避免潜在的由于内部类导致内存泄漏