近日在开发过程中,发现每次点击app从桌面启动都有一个在桌面明显的等待时间,机型越低端的越明显,冷启动优化看来已经势在必行,所以怒而一顿研究再解决之。话不多说先上优化前后效果图:
买家秀(gif):
淘宝秀(gif):
俗话说要想优化好,流程不可少!关于android app启动的流程图如下:
总结一下一个完成的冷启动app过程应该是经过:
Zygote Fork Proccess
-> Application:attachBaseContext()
-> Application:onCreate()
-> MainActiviity:onCreate()
这里主要是把相应的生命周期回调写出来。 因为一般大家的业务代码都是在这些函数回调中调用。
· Application:attachBaseContext():
这个方法中一般雷区主要都在这句代码上,因为在Android 5.0(API 21) 以下会存在65535方法数分包的问题,当dex过大时会导致Application启动慢, ANR或者ClassNotFound等异常,关于分包解决和优化方案网上一堆就不在做赘述了。
· Application:onCreate()
这个方法是需要重点优化的,因为大家的第三方插件初始化一般都会放在这里,在Application初始化做繁重的东西会严重阻塞app启动(DiskIO,网络请求等)。以下是我们第三方插件初始化的耗时:
debug:
release:
这里把debug和release下的时间都贴出来,是希望大家不要重蹈覆辙。。。 因为debug模式下和release模式下一个是1000ms左右, 一个是300ms左右,误差很大,请大家一定要在release下测试数据!!!!
针对于解决第三方插件初始化耗时方案一般是: 1.SDK分优先级加载,非必要SDK由懒加载实现。 2.可以多线程初始化的sdk由多线程方式来进行初始化。
· MainActiviity:onCreate()
同上一样,尽量不要在此布局做一些耗时的操作或者呈现一些过于复杂的布局。在具体分析自己的app时发现onCreate中有这样一行代码:
其作用是希望用户在打开时,一定能看见Splash的画面,主动延迟了1s加载。这里其实有更好的解决办法处理,则是把跳转MainPage的方法放在onResume中而不是放在onCreate中。因为Android系统中onResume一定是处于可见可交互的状态,用户一定能看见SplashActivity再去跳转,由系统生命周期决定,而不是固定的等待1s,此处优化后启动速度又提升了500ms。所以建议大家以后还是遵循生命周期去做一些事情,尽量别进行人为延迟阻塞。
App启动方式一般有3种:
· ColdStart ——冷启动:
此种方式最为耗时,一般是因为进程被干掉,系统需要重新fork进程进行一系列初始化。
· WarmStart ——暖启动
比ColdStart稍快,因为app的所有Activities还常驻在内存中,并没有被杀掉,所做的只是把app从后台提到前台来展示,并不需要重走初始化一系列行为,减少了对象初始化、布局加载等工作。但其行为表现与冷启动一致,是会displays a blank screen直到App渲染activity。这个blank screen后面会解释。
· LukeWarm Start——热启动
启动方式最快,类似于返回键退出应用又立即进入的那种行为。
优化方案:
既然冷启动那么慢,我们就在非用户主动kill进程或系统通知kill进程的其他情况下不再主动退出进程。那答案很简单了,就是在位于Activity栈底activity中Hook其返回键行为,保证用户点击返回键后不再退出app。在我们App里位于我们栈底的一定是我们的MainActivity,因为一系统行为都是由其向下衍生的。所以只需加入以下几句话:
moveTaskToBack:作用是不再Finish到此Activity,仅仅是把它放到后台隐藏。类似于用户主动触发系统Home键的效果。在同是点击返回键优化前后的对比如下:
优化前(gif):
优化后(gif):
若图1中时间久后进程回收后优化效果会更为明显。
经过上面一顿操作后,我发现然并卵!!!启动速度是提升了,但是App一点击还是会在桌面停顿一下。哇呀~很难受~细细思考了一下,一个APP启动无论如何都是会新Fork进程,难道就是这个问题导致其在桌面上停顿一会儿?那其他app又是怎么做到秒开的呢?在AndroidDeveloper的Launch-Time Performance有这么一句话:
其实在创建App进程时,android系统会为你立即显示一个background window,然后再去创建app进程,当app完成first draw时,会立即由你的MainActivity(即默认启动的Activity)替换掉它。这里的background window就是上文WarmStart中提到的blank screen。谜底到此解开所谓的秒开原来就是视觉欺骗。。。所以说有人给你说他只是仅仅是优化生命周期内初始化代码达到秒开都是扯淡。但不得不承认这样用户体验大大的提升了,一点击launcher就渲染好一个背景图片,给用户一种已经启动的感觉,前面做的一系列优化,不过为了让用户少看一会儿系统给渲染的black window。
那为什么我们的APP会出现在系统桌面上停留一会,而不是渲染背景图呢?原来在项目创建时,系统会为launcherActivity默认了一个LightTheme,这样就会导致App点击启动后会白屏一段时间然后展示自己的Activity,为了解决白屏的问题把theme主题换成透明的就像下面
但其实这样虽然解决了白屏的问题,但是就会出现上文所说的,点击后停留在桌面一会儿,直至MainActivity渲染出来。这是大部分App的做法,但并不是最佳解决方案。
WindowBackground最佳解决方案:
应该由windowBackground此属性作为你的品牌推广页或者logo页,如果你的SplashActivity完全不需要做任何初始化,只是希望有个闪屏页,完全可以由windowBackground来满足。
1.设置自定义带windowBackground的Theme
前两行代码是设置Theme不透明并且默认渲染的背景图是我们必看影视闪屏页的图片。
2. 为你的launcher Activity设置你的启动Theme
3. 在Launch Activity启动后再把主题设置回自己的AppTheme
此时你的App就能完成秒开了!
最终我们App从launcher点击到第一个Activity渲染完成总共需要时间是888ms,很吉利!
· Time to full Display
上图的那行日志是由系统打印出来的,意思就是渲染Acitivity所需要的时间,如果是第一个Activity启动时长也是算在内的。 在Android4.4(API19)以上才会有此Log打出,在verbose级别搜索Displayed即可。不仅可以看自己的还可以看到其他APP的启动时长。
附带一张小米4上业界各大App的冷启动时长~仅供参考~
· Hugo
执行时间打印神器,集成至项目后,只需在想要测试的地方加上注解即可:
可以是class级别,也可以是函数级别。如果注解是Appication类级别,还会把各大生命周期回调函数执行时间打印出来及其方便!(https://github.com/JakeWharton/hugo)
效果如下:
· Stetho
这是非死不可出的调试神器,如果开发android这个没用过的话,你一定会觉得相见恨晚!
如果您觉得我们的内容还不错,就请转发到朋友圈,和小伙伴一起分享吧~