目的:
为了提高apk的性能,使得apk在任何情况下,包括:cpu使用频繁,内存较少,用户重度使用手机(即用户使用一年后的情况,包含大量的图片,音乐和应用数据)都能够快速启动。
慢启动标准:1000ms。
一般启动标准:对标友商的启动时间。(一般为100ms)
统计方法:
1、monkey测试平台自动统计启动最久的时长和超时次数。
2、在event log中,搜索“am_activity_launch_time”找出所有的启动时长,再根据包名进一步筛选。
3、在main log中,搜索“Displayed 包名/Activity类”,也可以找出所有的启动记录
其中:测试部使用的是第一种方法。自己排查复现时,只能使用第二种或者第三种方法。
问题复现:
一般启动分析:
方法1:
在Android Device Monitor,选中设备,点击Capture System
配置trace,点击OK。然后操作手机,进行启动。
方法2:
仅限努比亚手机。在onResume延迟1s执行reflectDumpSysTrace()方法,即可在手机内存dump systrace文件。
然后分析执行时间较久的原因。
方法:
public static void reflectDumpSysTrace(){
ActivityManager activityManager =(ActivityManager)MyApplication.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
try {
Method
method = activityManager.getClass().getDeclaredMethod("dumpSystrace");
method.setAccessible(true);
method.invoke(activityManager);
} catch (Exception e){
e.printStackTrace();
}
}
慢启动分析:
预制步骤:导入重度用户数据,执行整机monkey(50w次)。
方法1:
在onCreate记录时间,然后在onResume记录时间,比较时间差。大于350ms时,使用上述方法输出systrace。 (onResume会执行多次,应该只计算第一次的执行时间。)
方法2:
使用设定在local.prop的方式或"adb shell setprop"命令设置以下两个系统属性
"debug.nubia.systrace_launch":activity的启动时间阈值,超过该值则dumpsystrace(单位:ms)
"debug.nubia.systrace_launch_pkg":应用包名(默认为空值),可设置多个,使用":"分隔;也可设置为"*",表示全部应用
工具使用
使用浏览器打开systrace。
操作:
m:选中当前片段/取消选择
问题排查
定位到应用:
一直下拉,直至找到包名。有时候包名不显示,只能根据pid查找。(无快捷方法)
主要查看UI Thread.
一直到第一帧绘制出来的时间就是启动时间(绿色的F)
查找执行时间:用鼠标选择从activity启动开始到第一帧绘制完成的时间片段。
结果如下:
总时间7s多,可以看出大部分时间都在休眠。(Running的时间就是cpu执行的时间)
查找休眠长的片段(空白的片段),然后右键查下一个片段(Runable)。查出是那个tid(wake from)阻塞了。然后在右上角查找tid,找出tid是什么。
如果是system service,一般和应用无关。
休眠原因查看:
如果所示:要查找3138(右上角有搜索框)
结果是一个system service。一般和应用无关,待进一步分析。
详情查看
在debug版本中
adb shell setprop debug.nubia.systrace_more 1
可以打出所有的系统方法。包括是那个方法导致休眠了。
可以看出虽然注册广播所需时间只需要.0643ms,但是在系统繁忙的时候,会导致放大上千倍。需要放在异步线程。但是这个广播不是应用控制的
跳帧分析
跳帧:Android中,有UI更新时,16ms会绘制一帧。跳帧就会给用户造成直观的卡顿现象。启动时间计算也是要第一帧显示完成。
定位:红色的F表示绘制失败
分析:
1、点击红色的F
2、查看警告,站看详情。
3、按照时间顺序,逐个分析,
比如图中的mesure took
212.60ms就是重点分析对象。
4、点击measure方法,定位到具体的方法。
5、按“M”选择当前时间段,通过左右移动和放大查看方法详情。
6、查看耗时原因。
名称解释:
Wall Duration 持续时间
CPU Duration cpu耗时
Self Time 自身方法耗时(不包含其调用方法)
CPU Self Time 自身方法cpu执行时间
案例总结
在该例子中,onMeasue是由于输入法弹窗重新布局触发的。
耗时原因主要是cpu阻塞和嵌套多层导致的。
排查记录:
1、页面更新UI的大量操作。信息需要异步读取,但是读取完成后,更新UI多,也是耗时的,会导致第一帧无法绘制成功。(采用Idle机制延迟加载)
2、标题栏延迟加载,只有设置标题的时候才执行onDraw方法。
3、输入法弹出时重新布局,采用Idle延迟加载
4、除了更新布局外,其他统一由线程池执行,包括:广播,sp,任何跨进程操作。
5、ActionBar,一般情况下需要耗时18ms。优化后采用自定义view,只需要4ms。
6、对比新旧数据,相同的数据不更新。
7、界面嵌套减少,一般要小于三层。
8、对于需要多个组合与嵌套实现的布局,最好的优化方法就是自定义View,只需要一次绘制。(所有的View都是继承View,结合canvas绘制出来的)
9、避免使用AppCompatActivity,这个一定会绘制ActionBar,只能隐藏,但是经过了绘制,再隐藏。耗时,无意义
总结
在掌握性能优化方法的同时,也要学习排查性能问题的方法。就是说,掌握了性能方法,并不能完全避免各种各样的性能问题,只有经过测试,排查,才能把性能问题减少到最低。
性能优化原则:
最小嵌套,
最少组合,
尽量异步,
重复利用,
更新延迟加载,
更新局部加载。
话外:使用一个“activity+多个Fragment“代替”多个activity” ,可以极大避免慢启动问题。