后台状态后打开应用程序时黑屏

Black screen when opening app after background state

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

当涉及到后台模式时,我很难理解 iOS 应用程序的完整生命周期。

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

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
}

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

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

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

我发现的大多数在线资源都没有具体说明应用程序如何在后台运行,例如 this graph 总是 过渡到 didBecomeActive在所有情况下。

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

引出问题2;如果我们应该在 didBecomeActive 中加载 UI,我们是否也应该卸载或释放 didEnterBackground 中任何活动的 UI?

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

或者我可能完全偏离了目标,对于某些用户有时没有 UI 的原因可能有不同的原因?

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

显然我们无法知道情况是否如此,但我想说你是在正确的轨道上。根据您的描述,我假设发生了以下情况:

计划任务启动了应用程序,但未构建 UI。 iOS 没什么可做的,所以它决定让您的应用程序以挂起模式运行,而不是在计划任务完成后完全终止它。我会说这实际上是意料之中的,因为它只会在它真的缺乏资源时终止它(我假设一个挂起的应用程序几乎什么都不用,内存被交换到磁盘,没有 CPU 周期,所以为什么完全终止它?)。

然后,稍后,用户点击该应用程序,它就会进入前台。因为它被挂起并且没有终止 didFinishLaunchingWithOptions 没有被再次调用(因为它已经很早地启动了)并且“哦不”没有构建 UI。


因此,具体回答您的问题 1(警告,自以为是!):

好吧,检查状态 的最佳实践在您的实施中显然没问题(if 检查)... 但是:基于状态构建 UI 不是 好的做法。事实上,在我看来,UI 从代码中自己“启动”构建是一个坏主意, 通常 。 Swift(UI) 应用程序和 UIKit & Storyboard-based 应用程序都不这样做是有原因的。我知道有些人赞成“在代码中执行 UI”,但我不同意。设法做到这一点的人通常并不意味着太直白,而且有一大群人误解了这一点,结果有一条又一条的观点互相推诿。这变得效率低下,因此像您这样的解决方案会突然出现,其意图是“好吧,在后台任务中我们必须要快,所以让我们把它弄糊涂”,而正确的结论应该是“也许我们做错了什么那份繁琐的手册 UI 构建“...

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

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

关于问题 2:

我猜这是一个又硬又脆的东西……

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

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

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


更新我是个白痴:

虽然以上所有内容可能都有用,但我有点看不到您实际想要测试的内容:基于状态的 UI 设置是否是造成问题的原因。有一种方法可以通过启动参数“注入”状态来确认这一点。您需要对您的 didFinishLaunchingWithOptions 实现进行最低限度的更改,并对您的应用方案进行一些改动,但它应该可以解决问题:

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

这样,您可以根据启动参数以与 BG 任务启动相同的方式启动。虽然这完全消除了 BG 任务从 OS 触发的实际启动,但它仍然足以让您弄清楚这个特定的启动流程导致了黑屏。

最后,如果您的应用程序还支持后台获取,您可以使用它来触发应用程序从 OS 启动并通过 Xcode 进入后台模式。该方法与上面我对问题 2 的回答中描述的类似,但您不安排任何任务。相反,您只需按照第 3 步让调试器等待,然后在 Xcode、select 中的“调试”菜单中“模拟后台提取”。也许这对你也有帮助,祝你好运!