Android程序不能无限制的使用设备的内存和CPU。正如经典所说,设备的资源不是给程序员用的,是给用户用的。在编写程序时,应该经常注意内存和CPU的使用。
在Android中,过多的使用内存容易导致OOM
,过多的使用CPU容易导致手机卡顿甚至ANR
。常见的优化方面有:
- 布局优化
- 绘制优化
- 内存泄漏优化
- 响应速度优化
- 列表List优化
- Bitmap优化
- 线程优化
布局优化
-
布局的核心是尽量扁平,不要嵌套。
- 删除无用的控件和层级
- 使用性能较高的
ViewGroup
。能用LinearLayout
代替RelativeLayout
的就尽量用。但是如果使用LinearLayout
会产生嵌套布局,那还是用Relativelayout
好,布局嵌套越少越好。
-
使用
include
merge
ViewStub
标签<include>
用于复用布局。<merge>
和<include>
一起使用,用于剔除多余的ViewGroup。如果当前布局是竖向的LinearLayout
,include进来的布局根布局也是竖向的LinearLayout
,使用<merge>
就能去掉多余的这层布局。<ViewStub>
用于按需加载布局。使用 ViewStub 标签引入的布局,默认不会被加载,只有通过 ViewStub 的setVisibility
或者inflater
方法加载后,ViewStub 就会被它引入的布局替换掉。
绘制优化
在View的onDraw中要避免执行大量的操作:
- 不用创建大量的局部变量。
onDraw
方法会被频繁调用,如果一瞬间产生大量临时对象,不仅占用过多内存,还用于引起GC,从而造成卡顿。 - 不要做耗时操作,也不能执行千万级循环。每帧的绘制时间不要超过16ms(1000/60fps)。
内存泄漏优化
- 静态变量引用了
Context
Activity
对象,会造成内存泄漏。 - 单列模式导致的内存泄漏。单列的生命周期和 Application 的生命周期一致。单列如果引用了Activity,Activity销毁之后,一直到程序结束都不会被回收。造成内存泄漏。
- 属性动画导致内存泄漏。属性动画如果在Activity结束时没有被
cancle
,就会导致 Activity 不能被释放,造成内存泄漏。解决方法是,在 Activity 的 onDestroy 中调用animator.cancel()
停止动画。 - Handler 导致的内存泄漏。在 Activity 中创建的Handler持有Activity的引用。如果当 Activity 结束之后,handler所在的线程还在执行,就会造成 Activity 不能被释放。解决方法是,Handler持有Activity的弱引用(
WeakReference
)。
响应速度优化
- 避免在主线程做耗时操作。
Activity
5秒没有响应触摸或键盘输入就会触发ANR
。BroadcastReceiver
10秒没有执行完操作也会触发ANR
。 - 当一个进程发生
ANR
之后,系统会在data/anr
目录下创建一个tracks.txt
文件记录原因。可以上传该文件到服务器进而分析ANR原因。
列表优化
- ListView 采用 ViewHolder 避免在
getView
中执行耗时操作 - 通过列表的滑动状态来控制加载任务。比如滑动时,不加载网络图片,不进行异步任务。
- 可以尝试开启硬件加速,提升滑动质量。
Bitmap优化
- 边界压缩。通过设置
options.inJustDecodeBounds = true;
只加载图片边界,获取图片的尺寸等信息。 - 质量降低。通过修改
Bitmap.Config
修改解码的颜色空间。 - 缩放图片。通过修改
options.inSampleSize
压缩图片尺寸。 - 使用缓存。使用
LurCache
在内存中缓存Bitmap,使用DiskLruCache
实现磁盘缓存。
线程优化
- 使用线程池控制线程数量和复用线程
其他优化
- 常量使用
static final
修饰 - 使用Android的特有数据结构:
ArrayMap
代替HashMap
SpareseArray
代替以Integer
为key的ArrayList。如ArrayList<Integer,Object>
。Pair
成双成对的数据。
- 尽量采用静态内部类。可以避免非静态内部类隐式持有外部类的引用,造成内存泄漏。(比如推荐在Activity使用静态内部类的Handler)。
- 合理使用
WeakReference
和SoftReference
。