现在微信小程序火爆异常,各种demo层出不穷,我也不免俗,一直有关注,虽然不是微信的死忠,但是不得不佩服微信的野心。开发难度不高再加上微信生态,感觉这个东西正式进入市场后必定对原生开发造成一定的影响,特别是一些创业公司因为考虑到原生开发的高成本,微信小程序不失为一种高效的途径。建议大家也保持关注,并进行一些基础的尝试。
好了,言归正传。最近公司的一款产品应为启动时间过长,所以做了一定的优化,而我有幸参与,现在主要记录一下实践过程。
通常来说,启动方式分为两种:冷启动和热启动。
1、冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。
2、热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。
本文所指的优化针对冷启动。简单解释一下App的启动过程:
1.点击Launcher,启动程序,通知ActivityManagerService
2.ActivityManagerService通知zygote进程孵化出应用进程,分配内存空间等
3.执行该应用ActivityThread的main()方法
4.应用程序通知ActivityManagerService它已经启动,ActivityManagerService保存一个该应用的代理对象,ActivityManagerService通过它可以控制应用进程
5.ActivityManagerService通知应用进程创建入口的Activity实例,执行它的生命周期
启动过程中Application和入口Activity的生命周期方法按如下顺序调用:
1.Application 构造方法
2.attachBaseContext()
3.onCreate()
4.入口Activity的对象构造
5.setTheme() 设置主题等信息
6.入口Activity的onCreate()
7.入口Activity的onStart()
8.入口Activity的onResume()
9.入口Activity的onAttachToWindow()
10.入口Activity的onWindowFocusChanged()
理论上来说当回调到入口Activity的onResume()方法时,App就算正式启动了,但是从这种意义上其实是不严谨的,因为在这个时间点
实际只完成了主题信息以及view树建立等。而view绘制的真正过程measure layout draw过程并没有,所以不是真正意义上的“可见”。 在onWindowFocusChanged()回调处更接近事实。所以我们确认启动时间可以从Application的构造方法记录开始时间,然后到入口Activity的onWindowFocusChanged()再记录一个时间,两者之差就是App的启动时间。当然这是比较笨的方法,adb命令里有相应的记录命令 :adb shell am start -W 包名/入口类全路径名 ,打出来的时间会有三个,分别是ThisTime,TotalTime,WaitTime,我们要取TotalTime作为标准。
有了这些准备知识,我们就要了解App启动为什么会缓慢。就我公司产品来说通过多次打点记录时间差,发现了主要的原因是在application和activity的启动生命周期中过多耗时的操作,重灾区是Application的attachBaseContext()和onCreate()方法,因为业务的需要集成了大量第三方sdk以及本公司封装的一些sdk,比较耗时的有okhttp模块初始化(应业务需要改装过),公司封装的一天混合开发框架(其中会解析本地的一份很长的配置文件,解压本地的H5页面资源包,初识化定位信息),还有很多其他的操作。重新审视代码发现有些操作不必再这两个方法做,完全可以等到页面出来后再在后台去做,即可以放到入口Activity的onWindowFocusChanged()方法中做。优化前后在魅族MX4上对比,优化前平均值大概是5.6s,优化后平均值大概是1.9s。
其实App启动缓慢的原因肯能多种多样,但是思路都是一样的,最主要的是掌握App启动的过程,然后再针对自己的产品分析,哪些初始化步骤可以在页面出来后再去做。
另外在优化过程中发现了application oncreate()方法多次执行,导致多次不必要的初始化,耗电耗内存耗速度。原因是应用程序中除了主进程还有两个子进程分别是push子进程(第三方push sdk的进程空间),remote子进程(百度定位sdk的进程空间)主进程启动后,会再启动这两个子进程, 重新执行了onCreate()方法。实际这里面的初始化代码跟它们都无关。百度sdk的initialize过程,在我们主进程中调用的)。解决办法就是在onCreate()时通过判断当前进程名,如果不是主进程则不走后续的逻辑。相信很多产品可能都集成推送sdk以及地图定位的sdk,这些sdk一般都会运行在新的进程,所以这个问题可能很多产品都有,值得关注。