App启动优化

2019-11-29  本文已影响0人  楚丶liu香

我们在使用某些超级App时,第一感受就是这款App怎么这么长时间才启动起来,等待的时间过长可能我们自己就把App杀掉了,有的甚至可能没有第二次启动机会就被卸载掉了,这个启动时间是非常影响用户体验的。虽然我现在开发的App还不是超级App,启动时间也说不上很慢,但是毕竟启动时间对于用户来说是越短越好,本着这个目标,我希望这次的优化能够更好的优化App启动时间,给用户带来更好的用户体验。

1、App启动类型

iPhone App的启动可以分为两种类型:冷启动和热启动。

1.1 冷启动

App启动前,其进程已被杀死或根本就没有在系统中存在,这种情况下点击App图标启动,就是一次冷启动过程。

1.2 热启动

App启动后,将App切到后台,但是进程没有别杀死,进程还在系统中存在,这种情况下重新点击App图标进入App,就是一次热启动过程。

针对上述两种启动,我们一般优化是指的优化第一种类型的启动过程。

2、App启动过程

冷启动的过程就是从用户点击App图标开始,一直到- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法执行完成为止。整个过程可以分为两个阶段,main()函数执行之前和main()函数执行之后。

app_launch_01.png

2.1 main()函数执行之前

  1. 加载可执行文件(App的.o文件集合)
  2. 加载动态链接库,进行rebase指针调整和bind符号绑定
  3. Objc运行时的初始处理,包括Objc相关类的注册、Category注册、selector唯一性检查等
  4. 初始化,包括执行+load()方法、attribute((constructor))修饰的函数调用、创建C++静态全局变量

备注:

  1. rebase指针调整:修复指向当前镜像内部的资源指针。
  2. binding符号绑定:指向镜像外部的资源指针
  3. image 二进制文件,包括可执行文件或so文件,里边是被编译过的符号、代码等。
  4. imageLoader 将image加载进内存,且每一个文件对应一个ImageLoader实例来负责加载。

dyld(动态连接器,the dynamic link editor)是一个专门用来加载动态链接库的库,dyld从可执行文件的依赖开始,递归加载所有依赖的动态链接库。动态链接库包括:iOS中用到的所有系统framework,加载OC runtime方法的libobjc,系统级别的libSystem

相应的,这个阶段对于启动速度优化来说,可以优化的点包含以下几个方面:

2.2 main()函数执行之后

这个阶段是指从main()函数执行开始,到Appdelegate的application:didFinishLaunchingWithOptions:方法执行完成。App的启动逻辑、首屏渲染和业务代码都是在这个阶段,主要包括:

  1. 全局初始化配置,各种组件库的初始化(crash统计、埋点统计)
  2. 首页数据请求、获取和处理
  3. 首页UI计算和渲染

对于第一条,我们要梳理出哪些配置和初始化工作是首屏渲染必要的,哪些是App启动所要求的,除此之外的配置和初始化都分别滞后到合适的阶段执行即可。

优化点:

3、App启动优化

说了这么多,App启动优化的时间如何来衡量呢,这就要用到上面提到的时间T1和T2,下面我们来说下如何来计算T1和T2。

测试使用机器:iPhone 7 Plus,iOS 10.3.2系统,存储容量32GB。

3.1 T1

main()之前的时间T1苹果官方提供了一种方法,在Xcode的Edit Scheme...-->Run-->Arguments选项中设置Environment Variables,添加name为DYLD_PRINT_STATISTICS,Value为1

app_launch_02.png

项目使用真机运行,在控制台就可以看到如下log了。

Total pre-main time: 4.2 seconds (100.0%)
         dylib loading time: 1.9 seconds (46.9%)
        rebase/binding time: 1.6 seconds (39.3%)
            ObjC setup time:  81.78 milliseconds (1.9%)
           initializer time: 498.71 milliseconds (11.7%)
           slowest intializers :
             libSystem.B.dylib :  19.39 milliseconds (0.4%)
                       AppName : 918.63 milliseconds (21.6%)

可以看到Total pre-main time总耗时为4.2秒。

3.2 T2

main()之后到- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions是T2阶段的时间,我们可以采用记住头尾时间取差值的方式得到T2,main函数代码如下:

extern CFAbsoluteTime startTime;

bool isBeauifulStr(char *str);
int main(int argc, char * argv[]) {
    startTime = CFAbsoluteTimeGetCurrent();
    @autoreleasepool {
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([MIAppDelegate class]));
    }
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中在最后加入如下代码:

CFAbsoluteTime startTime;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    ...
        
    dispatch_async(dispatch_get_main_queue(), ^{
        NSUInteger ms = (NSUInteger)((CFAbsoluteTimeGetCurrent() - startTime) * 1000);
        NSLog(@"T2 = %lu ms", ms);
    });
 
    return YES;
}

项目启动后,输出结果

T2 = 1190 ms

参考上面的优化点逐条去对启动过程进行优化,然后对比得到的T1和T2的时间,就可以知道你的优化效果到底如何了。

参考

iOS 程序 main 函数之前发生了什么
今日头条iOS客户端启动速度优化
App 启动速度怎么做优化与监控?
优化App的启动时间

上一篇下一篇

猜你喜欢

热点阅读