随着项目版本的迭代,App的性能问题会逐渐暴露出来,而好的用户体验与性能表现紧密相关,从本篇文章开始,我将开启一个Android应用性能优化的专题,从理论到实战,从入门到深挖,手把手将性能优化实践到项目中,欢迎持续关注!html
那么第一篇文章我就从应用的启动优化开始,根据实际案例,打造闪电般的App启动速度。android
来看一下Google官方文档《Launch-Time Performance》对应用启动优化的概述;git
应用的启动分为冷启动、热启动、温启动,而启动最慢、挑战最大的就是冷启动:系统和App自己都有更多的工做要从头开始! 应用在冷启动以前,要执行三个任务:github
而这三个任务执行完毕以后会立刻执行如下任务:shell
而一旦App进程完成了第一次绘制,系统进程就会用Main Activity替换已经展现的Background Window,此时用户就可使用App了。数据库
一样,Google也给出了启动加速的方向:缓存
- 利用提早展现出来的Window,快速展现出来一个界面,给用户快速反馈的体验;
备注:方向1属于治标不治本,只是表面上快;方向二、3能够真实的加快启动速度。 接下来咱们就在项目中实际应用。性能优化
按照官方文档的说明:使用Activity的windowBackground主题属性来为启动的Activity提供一个简单的drawable。 Layout XML file: 微信
这样在启动的时候,会先展现一个界面,这个界面就是Manifest中设置的Style,等Activity加载完毕后,再去加载Activity的界面,而在Activity的界面中,咱们将主题从新设置为正常的主题,从而产生一种快的感受。不过如上文总结这种方式其实并无真正的加速启动过程,而是经过交互体验来优化了展现的效果。 备注:截图一样来自官方文档《Launch-Time Performance》。markdown
经过代码分析咱们能够获得App启动的业务工做流程图:
这一章节咱们重点关注初始化的部分:在Application以及首屏Activity中咱们主要作了:
项目中**除听云以外其他全部三方组件都抢占先机,在Application主线程初始化。**这样的初始化方式确定是太重的:
项目修改:
注意:闪屏页的2秒停留能够利用,把耗时操做延迟到这个时间间隔里。
本节咱们实际定位耗时的操做,在开发阶段咱们通常使用BlockCanary或者ANRWatchDog找耗时操做,简单明了,可是没法获得每个方法的执行时间以及更详细的对比信息。咱们能够经过Method Tracing或者DDMS来得到更全面详细的信息。 启动应用,点击 Start Method Tracing,应用启动后再次点击,会自动打开刚才操做所记录下的.trace文件,建议使用DDMS来查看,功能更加方便全面。
左侧为发生的具体线程,右侧为发生的时间轴,下面是发生的具体方法信息。注意两列:Real Time/Call(实际发生时间),Calls+RecurCalls/Total(发生次数); 上图咱们能够获得如下信息:
即使是耗时操做,可是只要正确发生在WorkThread就没问题。所以咱们**须要确认这些方法执行的线程以及发生的时机。这些操做若是发生在主线程,可能不构成ANR的发生条件,可是卡顿是再算不免的!**结合上章节图App冷启动业务工做流程图中业务操做以及分析图,再次查看代码咱们能够看到:部分耗时操做例如IO读取等确实发生在主线程。事实上在traceview里点击执行函数的名称不只能够跟踪到父类及子类的方法耗时,也能够在方法执行时间轴中看到具体在哪一个线程以及耗时的界面闪动。
分析到部分耗时操做发生在主线程,那咱们把耗时操做都改到子线程是否是就万事大吉了?非也!!
经过对traceview的详细跟踪以及代码的详细比对,我发现卡顿发生在:
以及其它细节问题:
项目修改: 1. 数据库及IO操做都移到工做线程,而且设置线程优先级为THREAD_PRIORITY_BACKGROUND,这样工做线程最多能获取到10%的时间片,优先保证主线程执行。
2. 流程梳理,延后执行; 实际上,这一步对项目启动加速最有效果。经过流程梳理发现部分流程调用时机偏早、失误等,例如:
3.其它优化;
经过以上三步及三方组件的优化:Application以及首屏Activity回调期间主线程就没有耗时、争抢资源等状况了。此外还涉及布局优化、内存优化等部分技术,因对于应用冷启动通常不是瓶颈点,这里不展开详谈,可根据实际项目实际处理。
经过ADB命令统计应用的启动时间:adb shell am start -W 首屏Activity。 同等条件下使用MX3及Nexus6P,启动5次,比较优化前与优化后的启动时间;
优化前: MX3
ThisTime | TotalTime | WaitTime |
---|---|---|
1237 | 2205 | 2214 |
1280 | 2181 | 2189 |
1622 | 2508 | 2513 |
1485 | 2434 | 2443 |
1442 | 2418 | 2429 |
Nexus6P
ThisTime | TotalTime | WaitTime |
---|---|---|
1229 | 1832 | 1868 |
1268 | 1849 | 1880 |
1184 | 1780 | 1812 |
1262 | 1845 | 1876 |
1164 | 1766 | 1807 |
优化后: MX3
ThisTime | TotalTime | WaitTime |
---|---|---|
865 | 1516 | 1523 |
911 | 1565 | 1573 |
812 | 1406 | 1418 |
962 | 1564 | 1574 |
925 | 1566 | 1577 |
Nexus6P
ThisTime | TotalTime | WaitTime |
---|---|---|
603 | 1192 | 1243 |
614 | 1076 | 1115 |
650 | 1120 | 1163 |
642 | 1107 | 1139 |
624 | 1084 | 1124 |
对比: MX3提高35%
ThisTime平均数 | TotalTime平均数 | WaitTime平均数 |
---|---|---|
优化前 | 1413 | 2349 |
优化后 | 895 | 1523 |
Nexus6P提高39%
ThisTime平均数 | TotalTime平均数 | WaitTime平均数 |
---|---|---|
优化前 | 1221 | 1814 |
优化后 | 626 | 1115 |
一、还能够继续优化的方向?
二、异步、延迟初始化及操做的依据? 注意一点:并非每个组件的初始化以及操做均可以异步或延迟;是否能够取决组件的调用关系以及本身项目具体业务的须要。保证一个准则:能够异步的都异步,不能够异步的尽可能延迟。让应用先启动,再操做。
三、通用应用启动加速套路?
四、其它
参考文章:《官方文档——Launch-Time Performance》
欢迎关注微信公众号:按期分享Java、Android干货!