线程iOS常识

iOS启动优化:App启动耗时在线监控与AppDelegate管

2020-05-21  本文已影响0人  笑出zhu声

一、App启动耗时在线监控

在大型的多团队合作的项目中,往往不经意间的一个个改动,可能就会直接或累加式的拖慢App的启动速度,测试人员通过本地的录屏或者开发工具测量启动耗时由于受测试机器的状态和样本数量的原因数据往往有波动,并不能真正反馈App启动时间的真实变化。所以加入在线的数据监控变得非常重要。

在介绍App启动耗时监控之前,我们先大概回顾下App启动过程:

启动过程

main()函数之前的阶段我们成为pre-main(),至于pre-main()中每个阶段的具体作用,这边就不再赘述,网上资料较多。

pre-main()开始时间:__t1

苹果并没有直接提供App启动的开始时间,目前业内主要有两种标准作为App的启动时间:

pre-main()结束时间:__t2

获取pre-main()结束时间相对容易,可以main()函数的开始执行时间,而我更推荐使用__attribute__((constructor)) 函数调用作为pre-main()的结束时间,这样能最大程度的实现解耦:

void static __attribute__((constructor)) before_main() {
    if (__t2 == 0) {
        __t2 = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;
    }
} 

至于为什么不用最后一个load方法执行时间?因为在大型工程中我们没办法确定哪个load方法是最后一个...

启动完成时间:__t3

启动完成时间一般可以通过获取didFinishLaunchingWithOptions:的结束时间,但是didFinishLaunchingWithOptions:的结束时间其实不包括启动图动画的时间,启动图动画执行完成后的时间更接近用户的感官。我们可以在执行didFinishLaunchingWithOptions: 的runloop循环的后面的循环来获取时间:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //do somethings
    
    dispatch_async(dispatch_get_main_queue(), ^{
        if (__t3 == 0) {
            __t3 = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;
        }
    });
    return YES;
}

二、AppDelegate管控

在iOS项目中,AppDelegate类里面的代码往往都是杂乱无章,各模块只要有需要,都会往AppDelegate中添加各种各样的方法,特别是didFinishLaunchingWithOptions: 方法更是重灾区。为什么要把AppDelegate的管控和启动耗时监控写在一起呢?因为,合理且统一的模块调用方式,可以更好的去统计每个模块的调用耗时,进而实现更精细化的监控。

  1. 抽象统一的方法;

    如需要在didFinishLaunchingWithOptions:被调用的模块都在自己的模块中实现+ (void)didFinishLaunchingWithOptions的静态方法。

  2. didFinishLaunchingWithOptions:中使用rouer或者动态调用各模块的对应的静态方法。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        
        //do somethings
        //...
        
        NSArray *modules = @[@"Module1",@"Module2",@"Module3",@"Module4",@"Module5"];
        for (NSString *module in modules) {
            //1.获取开始时间
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            //2.调用模块
            NSString *url = [NSString stringWithFormat:@"%@://didFinishLaunchingWithOptions",module];
            [Router openUrl:url];
            //3.获取消耗的时间
            CFAbsoluteTime cost = CFAbsoluteTimeGetCurrent() - start;
            //4.记录、上报
            ...
        }
        
        return YES;
    }
    
  3. 将AppDelegate制作成静态库,如AppDelegate.framework。main函数中修改(非pods工程,记得在Other Linker Flags中添加-force_load ,避免链接时被优化):

    int main(int argc, char * argv[]) {
     @autoreleasepool {
     return UIApplicationMain(argc, argv, nil, @"AppDelegate"));
     }
    }
    

AppDelegate这样模块可以交由专人维护,进而实现了AppDelegate的管控。

上一篇下一篇

猜你喜欢

热点阅读