指标
首先确立优化的内容以及目标
- 内存
不溢出 不抖动 - fps
此值依据项目的实际情况而定(我们公司的项目庞大,并且用户机器性能良莠不齐,所以当时定的40fps) - anr
避免出现anr
监测
收集性能相关的数据也是比较棘手的工作
推荐三个工具:
-
leakcanary
监测内存泄露 -
blockcanary
检测页面卡顿 一定程度上可以监测到anr -
Hugo
可以输出每个方法的执行时间
关于fps的监听:
Activity
实现Choreographer.FrameCallback
接口,重写doFrame
方法
@Override
public void doFrame(long frameTimeNanos) {
if (System.currentTimeMillis() - mTime >= 1000) {
mNumber = 0;
mTime = System.currentTimeMillis();
} else {
mNumber++;
}
Choreographer.getInstance().postFrameCallback(this); //这种方式相当于循环监听
}
Choreographer.getInstance().postFrameCallback(this);
关于内存抖动的监听:
暂时未找到十分高效的监听方式,我在项目中是利用Memory Monitor
人为观察的
自动化测试
找到定位问题的方法,我们还得有高效的收集数据的方式
我们可以结合Monkey
和MonkeyRunner
来实现一种高效的数据采集,具体可见Android自动化测试之页面覆盖比例
解决问题
项目中经常出现的问题
内存泄露
Context
Context
导致的内存泄露常出现在单例中,大家都知道单例中instance
是static
修饰的,而static
修饰的变量的生命周期对应整个应用程序的生命周期,所以解决办法是单例中传入context.getApplicationContext()
Handler
Handler
可以说是Android中内存泄露的恶魔
由Handler
的消息机制可知,msg
持有mHandler
的引用,而mHandler
是Activity的非静态内部类实例,即mHandler
持有Activity
的引用,当Activity
退出后,msg
可能仍然存在于消息对列MessageQueue
中未处理或者正在处理,此时就有可能导致Activity
无法被回收
解决的方法有两种:
- 静态内部类
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activityWeakReference;
public MyHandler(MainActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityWeakReference.get();
if (activity != null) {
if (msg.what == 1) {
// 做相应逻辑
}
}
}
}
- WeakHandler
WeakHandler
是第三方实现的库,使用上和Handler
一样,地址:https://github.com/badoo/android-weak-handler
思想:将Handler
和Runnable
做一次封装,我们使用的是封装后的WeakHandler
,但其实真正起到handler
作用的是封装的内部,而封装的内部对handler
和runnable
都是用的弱引用
Timer和TimerTask
同Handler
原理类似,Timer
也存在内存泄露的可能
解决的方法:除了使用上注意在Activity
销毁的时候立即cancel
掉Timer
和TimerTask
以外,还需要将TimerTask
写成静态内部类的形式
其实Timer
完全可以用Handler
替代
Bitmap
-
Bitmap
及时回收
要遵循以下的代码规范:
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
bitmap = null;
}
System.gc();
监听器
监听器的注销 例如广播
属性动画
属性动画及时cancel
如何减少对象的内存占用?
- 使用轻量的数据结构
ArrayMap
或者SparseArray
代替HashMap
key
为int
值时使用SparseArray
,可以避免自动装箱 - 减少
Bitmap
的内存占用
适当对Bitmap
进行质量压缩(比例压缩)
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2; //这个的值压缩的倍数(2的整数倍),数值越小,压缩率越小,图片越清晰
//返回原图解码之后的bitmap对象
bitmap = BitmapFactory.decodeResource(Context, ResourcesId, opts);
即先将图片缩小一倍,再将这缩小了一倍的图片作为bitmap
存入内存,这样一来,它占用的bitmap
内存大大减小
- 避免使用枚举
内存抖动
充分利用线程池和LRU
缓存算法对内存进行重复利用
-
ListView
中ViewHolder
复用ContentView
-
Bitmap
利用LRU
算法进行缓存处理 - 利用线程池对线程进行优化
- 避免类似
onDraw
或者for
循环之类频繁调用的方法体中创建对象
fps过低(页面卡顿)
- 耗时操作
主线程中不可进行耗时操作 - onDraw方法
切忌在View
的onDraw
方法中执行大量操作,比如创建新的局部变量 - 页面过度绘制
1.减少页面层级
使用ViewStub
和merge
2.去除多余的android:background
属性
3.确保页面层级的情况下尽量使用LinearLayout
,层级过多则使用RelativeLayout