APP启动优化
通过阅读这篇文章,我们将了解APP启动过程中都做了哪些事情。文章分为三部分,第一部分是原理讲解,第二部分是优化方案,第三部分是实践应用。
第一部分 原理讲解
APP启动的分类
APP分为冷启动和热启动。
冷启动是指, App 点击启动前,它的进程不在系统里,需要系统新创建一个进程分配给它启动的情况。这是一次完整的启动过程。
热启动,对于热启动的理解存在分歧,一种是是指户点击home键后再次回到前台显示,还原到退出前的状态,继续为用户服务;另一种是用户杀掉APP进程后在dyld没有删除缓存的情况下重启APP,这种情况下的启动速度会比冷启动要快一些。
对于热启动的分歧,笔者觉得从优化角度来说,第二种根据有研究的意义和符合实际使用情况。对比两个启动方式对用户的影响,冷启动的快慢会给用户造成第一印象的好坏,影响用户的使用体验和留存。接下来我们深入研究下APP冷启动过程。
从点击到启动
当用户点击手机桌面上的图标到首页展示到用户面前并且可以进行交互,这个过程我们定义为APP启动的一个完整过程。这个过程中发生了很多事情。系统先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境,开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。当所有依赖库的初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类结构初始化,然后调用所有的load方法。最后dyld返回main函数地址,main函数被调用,我们便来到了熟悉的程序入口。
我们将以上阶段分为Ready、Pre-Main、Main三个阶段。
Ready阶段
iPhone/iPad的桌面系统也是一个应用,我们称之为Springboard,当用户触碰屏幕点击APP的icon时,XNU加载Mach-O和dyld,然后系统会从XNU内核态将控制权转移到dyld用户态。dyld会负责后续工作,这时第一个阶段完成。
为了精简文章体积和侧重本文重点,我们简单了解几个概念:Mach-O 、dyld ,有兴趣的同学可以自行深入学习和研究。
Mach-O
Mach-O是Mach object的缩写,是Mac\iOS上用于存储程序、库的标准格式。
Mach-O有以下几种类型,其实动态库、静态库、程序本身、bundle文件其实都是属于Mach-O文件。
既然是文件,就会有固定的存储格式:Header、Load Commands、Raw Data三大部分。
Header:保存了一些基本信息,包括了该文件运行的平台、文件类型、LoadCommands的个数等等。
LoadCommands:可以理解为加载命令,在加载Mach-O文件时会使用这里的数据来确定内存的分布以及相关的加载命令。比如我们的main函数的加载地址,程序所需的dyld的文件路径,以及相关依赖库的文件路径。
Raw Data:这里包含了具体的代码、数据等等。
dyld
dyld是动态链接器。dyld从可执行文件的依赖开始, 递归加载所有的依赖动态链接库。系统内核在加载动态库前,会加载dyld,然后调用去执行__dyld_start(),该函数会执行dyldbootstrap::start(),后者会执行_main()函数,dyld的加载动态库的代码就是从_main()开始执行的。
dyld存放位置目前版本存放的路径是/usr/lib/dyld。系统会解析Mach-O文件中的Load Commands段的LC_LOAD_DYLINKER,获取到dyld的路径(如下图)。