首页 > 解决方案 > 在后台状态后打开应用程序时黑屏

问题描述

我的任务是调试为什么某些用户在打开应用程序时有时会遇到黑屏。我是这个特定应用程序的新手,所以我不知道整个流程,但我可以看出该应用程序具有后台功能。有一些任务在夜间运行。

在后台模式方面,我很难理解 iOS 应用程序的整个生命周期。

当应用程序从终止状态在后台启动时,我假设它didFinishLaunchingWithOptions仍然会被调用。我看到我们在代码中有一个小检查,在这种情况下它省略了整个 UI 初始化:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    /*{ Initial setup }*/
    if UIApplication.shared.applicationState == .background {
        // App was launched due to Background Fetch event. No need for UI.
        return true
    }
    /*{ Start UI }*/
    return true
}

我怀疑当应用程序当前或最近由“系统”在后台运行时(从最初终止的状态)手动打开时,此代码会导致应用程序不显示 UI。这个对吗?这意味着在某些极端情况下,人们会在没有调用“{ Start UI }”的情况下打开应用程序。

我们还applicationDidBecomeActive实现了,我认为应该使用它来确保在这种情况下呈现 UI。但是,现在这里只有一些可达性的东西:

func applicationDidBecomeActive(_ application: UIApplication) {
    reachabilityManager?.startObserving()
}

我发现的大多数在线资源都没有具体显示应用程序是如何在后台启动的,例如这个在所有情况下总是过渡到的图表。didBecomeActive

所以问题1;评估状态的最佳实践方法是didFinishLaunchingWithOptions什么?当前的实现是否在.background最佳情况下省略了 UI?如果是这样,我是否应该执行检查以查看 UI 是否正在运行didBecomeActive,如果不是则启动 UI?

引出问题2;如果我们应该在其中加载 UI didBecomeActive,我们是否也应该在其中卸载或取消分配任何活动的 UI didEnterBackground

额外的问题:是否有可能用调试器实际重现这个?每次我用调试器启动应用程序时,它显然不在后台。如何调试从后台到前台的生命周期?

或者我可能在这里完全偏离目标,为什么有时某些用户没有 UI 可能有不同的原因?

标签: iosswiftbackground-fetchios-lifecycle

解决方案


除了这里的评论还有一种答案:

显然我们不知道是否是这样,但我会说你走在正确的轨道上。根据您的描述,我假设会发生以下情况:

计划任务启动应用程序,并且不构建任何 UI。iOS 没有太多工作要做,因此它决定让您的应用程序处于暂停模式,而不是在计划任务完成后完全终止它。这实际上是我想说的,因为它只会在它真的缺乏资源时终止它(我假设一个暂停的应用程序几乎不需要任何东西,内存被交换到磁盘,没有 CPU 周期,那么为什么要完全终止它呢?) .

然后,稍后,用户点击应用程序并将其置于前台。由于它已暂停且未终止didFinishLaunchingWithOptions,因此不会再次调用(因为它已经在更早的时候启动了)并且“哦,不”没有构建 UI。


因此,要具体回答您的 Q1(警告,固执己见!):

好吧,检查状态的最佳实践在您的实现(检查)中显然是可以的if......但是:基于状态构建 UI不是一个好的实践。事实上,在我看来,一般来说,自己从代码“启动” UI 构建是一个坏主意. Swift(UI) 应用程序和基于 UIKit 和 Storyboard 的应用程序都没有这样做是有原因的。我知道有些人喜欢“在代码中做 UI”,但我不同意。能够做到这一点的人通常并不意味着太字面意思,并且有一大群人误解了这一点,并因此产生了一行又一行的观点。这变得效率低下,所以像你这样的解决方案弹出的意图是“好吧,在后台任务中我们必须快速,所以让我们把它骗出来”,而正确的结论应该是“也许我们做错了什么繁琐的手动 UI 构建”...

那么,这对你有什么帮助(因为我猜你不能完全重做所有 UI 的东西来修复这个错误)?不幸的是,“这取决于”。我要尝试的第一件事实际上只是省略了 UI 的状态检查,即不管应用程序是否从后台启动,都建立它。这里希望该过程中的任何内容实际上都不需要应用程序在屏幕上可见才能成功完成。如果这行得通,那又如何?然后,该应用程序在执行后台任务时有一个尚未显示的 UI,但那又如何呢?无论如何,一旦用户将其置于前台,它将在以后使用。

如果这不起作用,则尝试将 UI 构建移入didBecomeActive并进行一些检查,以确保在应用程序已经执行该代码的情况下它不是“重建”。在您的团队中公开质疑整个 UI 方法可能是值得的。

关于问题2:

我猜这是一件困难而脆弱的事情......

正如评论中所解释的,调试这个技巧是为了让调试器为一个不是由 Xcode 启动的应用程序启动做好准备,而是由计划的 BG 任务启动。这意味着两件事:

  1. 您必须以某种方式将任务安排到未来或多或少已知的时间点。这很可能需要您添加一些调试代码(除非您想为每晚安排的实际任务熬夜......只是错过它)。只需从内部安排一个didFinishLaunchingWithOptions在一分钟左右运行。现在,我知道您无法保证它会在那时执行,但我目前看不到其他方法。我自己没有尝试过,如果它不起作用,呃......你可能不走运(或者必须等待很长一段时间才能真正调用任务)。
  2. 启动应用程序以便安排任务,然后在目标时间过去之前立即再次终止应用程序。
  3. 更改 Xcode 方案。“运行-信息-启动”改为“等待可执行文件启动”。按下运行按钮。调试器现在等待应用程序通过其他方式启动。除非您自己点击图标,否则这将是计划任务。

顺便说一句,Apple 有一个具体的方法来帮助您调试 BG 任务,但看起来它实际上不会在调度它和启动任务处理程序之间终止您的应用程序(因此didFinishLaunchingWithOptions不会以您的方式调用需要)。不过,您可能想自己调查一下(这个答案已经足够长了……对此感到抱歉)。


更新,因为我是个白痴:

虽然以上所有内容可能有用,但我有点忽略了您真正想要测试的内容:基于状态的 UI 设置是否对问题负责。有一种方法可以通过启动参数“注入”状态来确认这一点。您需要对您的实现进行最低限度的更改didFinishLaunchingWithOptions,并在您的应用程序方案中稍微弄乱一点,但它应该可以解决问题:

  1. 为您的应用方案提供自定义启动参数,例如“fakeBGState”。为此,请编辑方案,转到“Run - Arguments”并在“Arguments Passed On Launch”下添加该字符串。
  2. 在您的didFinishLaunchingWithOptions, 将您的 UI 设置代码包装到
    if ProcessInfo.processInfo.arguments.contains("fakeBGState") {
        // ...
    }

这样,您可以按照与启动参数相同的方式启动 BG 任务。虽然这完全消除了操作系统为 BG 任务触发的实际启动,但它仍然足以让您发现这个特定的启动流程会导致黑屏。

最后,如果您的应用程序还支持后台提取,您可以使用它通过 Xcode 触发应用程序从操作系统启动到后台模式。该方法与我在对问题 2 的回答中描述的类似,但您不安排任何任务。相反,您只需按照步骤 3 让调试器等待,然后在 Xcode 中,从 Debug 菜单中选择“Simulate Background Fetch”。也许这对你也有帮助,祝你好运!


推荐阅读