美文网首页
App启动优化 - 实践二

App启动优化 - 实践二

作者: WSJay | 来源:发表于2019-10-28 15:57 被阅读0次

    概要:

    • 什么是启动?
    • 如何测量启动?
    • 使用Instruments分析启动
    • 跟踪启动的进度

    一、启动的重要性

    • 影响用户的体验
    • 表明代码的整体性能
    • 影响系统性能和电池

    二、启动类型

    • 冷启动(Cold)
      当重启设备或App长时间未启动(App不在内存中)时,则会发生冷启动。
      为了启动App,需要将它从磁盘读入内存,启动支持App的系统服务,然后生成App的进程。
    • 热启动(Warm)
      当App发生一次启动之后,再次启动就是热启动。此时App程序部分在内存中。
    • 继续运行(Resume)
      当App从主屏幕,或App切换器重新进入自己的App时,则发生这种情况。此时App程序完全在内存中。

    冷启动、热启动、运行之间的区别:

    三、启动过程

    image.png

    这六个过程包含了从系统初始化到App初始化,再到视图创建和布局,若App有需要,还可能会有一个扩展阶段,用于数据的异步加载。

    (一)System Interface

    第一阶段System Interface的前半部是DYLD3 ,关于DYLD3可以参考资料 App Startup Time: Past, Present, and Future

    • 静态链接器会加载App的共享库和框架;
    • 为热启动缓存运行时的依赖项;

    第一阶段System Interface的后半部是libSystem Init。在App中初始化底层系统组件的时候,现在这主要是系统方面的工作,有固定的消耗。因此我们不需要关注这一部分。

    优化建议:

    • 避免链接未使用的框架;
    • 避免在启动期间加载动态库;
      例如:dlopen()、或NSBundle、或load(),因为这样会损失在缓存中建立的那些优势
    • 硬链接所有的依赖;
    (二)Static Runtime Initialization

    第二阶段是静态运行时初始化,该阶段主要完成以下工作:

    • 系统初始化Objective-C和Swift语言运行时;
    • 调用所有类的静态加载方法;

    一般而言我们的App不应该在这里做任何工作,除非我们的程序中存在静态初始化方法,或者我们链接的框架带来的。通常不建议静态初始化。

    优化建议:

    • 暴露框架中的初始化API;
    • 避免使用+[Class load]方法,减少对启动的影响;
    • 使用+[Class initialize]方法延迟静态初始化;
      如果程序中有一个使用静态初始化的框架,则要考虑暴露API来尽早初始化我们的栈。如果必须要使用静态初始化,请考虑将代码移出+[Class load]。因为+load方法在App启动期间总会被调用。我们可以在类中第一次使用方法时来延迟调用。
    (三) UIKit Initialization

    第三阶段是UIKit初始化,该阶段的工作如下:

    • 系统初始化UIApplicationUIApplicationDelegate
    • 开始事件的处理和系统的集成;

    该阶段是系统初始化程序UIApplicationUIApplicationDelegate的时候。在大多数情况下,这是系统的工作,设置事件处理和系统的集成。如果我们在子类UIApplication,或者在UIApplicationDelegate初始化程序中做其他工作,仍然会影响这一阶段。

    优化建议:

    • 减少在子类UIApplication中工作;
    • 减少在UIApplicationDelegate初始化中的工作;
    (四)Application Initialization

    第四阶段是App初始化,最重要的东西都在这里,该阶段的工作如下:

    • 调用UIApplicationDelegate的App生命周期回调;

    这是作为开发者能够对App启动产生重大影响的地方。如果我们的App还没有采用UISceneAPI,或针对iOS 12及更早版本的用户来说,App初始化仍然可以用这些回调方法。

    application:willFinishLaunchingWithOptions:
    application:didFinishLaunchingWithOptions:
    
    • 调用UIApplicationDelegate的UI生命周期回调;

    当App展示给用户时,将会进一步调用下面的方法。

    applicationDidBecomeActive:
    
    • 为每个场景调用UISceneDelegate的UI生命周期回调

    当我们的App没有采用UISceneAPI时,我们应该在application:didFinishLaunchingWithOptions:方法中创建视图控制器。当使用UIScene时,App初始化的工作方式略有不同。我们仍然可以获得application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:方法,但是当App展示给用户时,我们将获得UISceneDelegate生命周期回调。

    scene:willConnectToSession:options:
    sceneWillEnterForeground:
    sceneDidBecomeActive:
    

    优化建议:

    • 推迟无关的工作;
      但没必要提交你的第一帧,可以通过将其推送到后台队列,或者稍后再完全执行。
    • 在场景之间共享资源;
      如果我们的App采用了UIScene,要确保在场景之间共享资源。这样做是为了减少多次不必要地进行一些工作的开销。
    (五) Frame Render

    第五阶段是第一帧渲染,这个阶段相对简单,该阶段的工作如下:

    • 创建视图,执行布局,绘制视图;
    loadView 
    viewDidLoad 
    layoutSubviews
    
    • 提交和渲染第一帧;

    优化建议:

    • 展平视图层次结构和延迟加载视图;
      我们可以减少层次结构中的视图数量来影响次阶段。也可以通过展平视图来减少使用,或延迟加载在启动期间未显示的视图来实现。
    • 优化自动布局的使用;
      查看自动布局,减少正在使用的约束数量;
    (六)Extended

    第六阶段是扩展,该阶段的工作如下:

    • 第一帧后App特定时间段;
    • 显示异步加载的数据;
    • 应用程序应具有交互性和响应性;

    这是从我们第一次提交到向用户显示最后帧的App特定时间段。这是当我们加载异步数据时。其实不是每个App都有这个阶段。如果我们App有这个阶段,那么我们的App应该具有交互性和响应性。

    优化建议:

    • 利用os_signpost衡量工作;
      当我们的应用确实有这个阶段时,我们需要了解正在发生什么,并且可以通过利用os_signpostApi来标记和衡量在这两个时期发生的工作。

    四、如何正确测量启动

    (一) 一致性
    • 消除差异来源以产生更一致的结果
    • 可能导致启动时间不具有代表性
    • 使用一致的结果评估进度
    image.png image.png
    (二)在一个干净一致的环境中测试

    通过以下方法,可以保证创建一个干净一致的测试环境:

    • 重启设备,并让系统休眠2-3分钟;

    重启设备可以清除不必要的状态,让系统休眠2-3分钟,来清除任何启动时间工作。

    • 打开飞行模式,或模拟网络;

    减少对网络的依赖。因为网络会引入相当多的差异。

    • 使用不变的iCloud帐户和数据,或完全注销iCloud帐户;

    iCloud在后台运行。这回干扰App的启动。

    • 使用应用的发布版本;

    这是为了减少测量期间不必需要的调试代码的开销,并利用编译时优化。

    • 测量热启动;

    这是因为它们更加一致,因为某些App可能已经在内存中,并且其中一些系统端服务可能已在运行。

    (三)用具有代表性数据进行测试
    image.png
    (四)测试较新和较旧的设备
    image.png
    (五)使用XCTest测量启动
    image.png

    在任何给定的时间,iOS设备都处于各种不同的状态和情况下,这可能会在启动时引起很大的差异。因此当我们分析和比较启动结果时,确保我们进行"Apple - To - App le"的比较是至关重要的。因为如果在进行任何更改之前,你的启动结果完全不可预测。我们如何知道自己是否取得了进展呢?使其可预测的第一步是消除这些差异的来源,例如网络干扰,后台进程中的干扰。现在我们意识到这听起来有悖常理,因为这可能会导致启动不能完全代表常规使用。但这没有关系,拥有一致的结果可以评估很好地进展,这一点尤为重要。在Apple中一直只用这种技术,在开发过程中成功检测回归,并缩短启动时间。然后通过使用在实际情况中收集的遥测数据来验证这些性能的改进。

    五、如何优化启动

    当我们在代码和工具中查看App的启动时,我们应该记住以下三个提示和技巧:

    (一)最小化你的工作
    • 推迟与第一帧无关的工作;
      推迟未显示的视图,或尚未使用的预加热功能等内容。

    • 避免阻塞主线程;
      比如网络I/O,文件I/O或其他。这些都会影响启动,可以将其移动到后台线程。

    • 减少内存的使用;
      分配和操作内存可能需要时间。

    (二)优先考虑你的工作
    (三)优化你的工作
    • 简化或限制现有工作
      例如限制仅在启动期间获取所需数据的数据量,或者懒计算所需的任何变量和结果
    • 优化算法和数据结构
    • 缓存资源和计算
      我们应该缓存资源和复杂的功能,减少CPU和内存开销。

    六、跟踪启动的进度

    • 使用Xcode Organizer监视用户的启动;
    image.png
    • 采用MetricKit获取更多的统计信息;

    七、参考资料

    相关文章

      网友评论

          本文标题:App启动优化 - 实践二

          本文链接:https://www.haomeiwen.com/subject/vthcvctx.html