【问题标题】:Download multiple files with operation queue not stable in background mode下载多个文件,操作队列在后台模式下不稳定
【发布时间】:2020-02-01 03:48:30
【问题描述】:

目前我想要实现的是从一个数组中下载文件,一次只下载一个文件,即使应用程序进入后台状态,它仍然会执行下载。

我正在使用 here 中所述的 Rob 代码,但他正在使用 URLSessionConfiguration.default 我想使用 URLSessionConfiguration.background(withIdentifier: "uniqueID")反而。

它在第一次尝试时确实有效,但在它进入后台后,一切都变得混乱了。操作开始一次下载多个文件,不再按顺序下载。

是否有任何解决方案或者我应该使用什么来实现我想要的。如果在 android 中,我们有服务可以轻松处理。

【问题讨论】:

    标签: ios swift nsurlsession nsoperationqueue


    【解决方案1】:

    将请求包装在操作中的整个想法仅适用于应用程序处于活动/运行状态的情况。它非常适合限制前台请求的并发程度、管理依赖项等。

    但是,对于在应用程序暂停后继续进行的后台会话,这些都无关紧要。您创建您的请求,将其交给后台会话进行管理,并监控为您的后台会话调用的委托方法。不需要/不需要任何操作。请记住,这些请求将由后台会话守护进程处理,即使您的应用程序已暂停(或者如果它在其正常生命周期过程中终止,但如果您强制退出它则不会)。因此,如果后台 URLSession 守护进程正在处理请求并且您的应用程序未处于活动状态,那么操作、操作队列等的整个想法就没有意义。

    有关后台会话的示例,请参见 https://stackoverflow.com/a/44140059/1271826


    顺便说一句,真正的后台会话在下载可能需要很长时间的非常大的资源时非常有用。但它引入了各种复杂性(例如,您经常希望在未连接到 Xcode 调试器时进行调试和诊断,这会改变您的应用程序生命周期,因此您必须求助于统一消息等机制;您需要弄清楚如何恢复 UI如果应用程序在请求发起和完成之间终止;等等)。

    由于这种复杂性,您可能需要考虑是否绝对需要这样做。有时,如果您只需要不到 30 秒的时间来完成某些请求,那么在用户离开应用程序后让操作系统让您的应用程序在后台运行一段时间并使用标准的URLSession 会更容易。有关详细信息,请参阅Extending Your App's Background Execution Time。这是一个更简单的解决方案,绕过了许多背景URLSession 的麻烦。但它仅在您只需要 30 秒或更短的时间时才有效。对于可能超出此小窗口的较大请求,需要真正的背景URLSession


    下面,你问:

    据我了解,[并行下载多个文件]有一些缺点。

    不,最好让下载以异步和并行的方式进行。它速度更快,效率更高。您唯一想要一个接一个地连续执行请求的地方是您需要解析一个请求的响应以准备下一个请求。但这里不是这样。

    这里的例外是默认的前台URLSession。在这种情况下,您必须担心后面的请求会因等待较早的请求而超时。在这种情况下,您可能会增加超时间隔。或者我们可以将我们的请求包装在Operation 子类中,允许我们不仅限制我们将允许多少并发请求,而且在之前的请求完成之前不启动后续请求。但即使在这种情况下,我们通常也不会连续执行此操作,而是使用 4 的 maxConcurrentOperationCount 或类似的值。

    但是对于后台会话,请求不会因为后台守护进程尚未处理它们而超时。只需将您的请求添加到后台URLSession 并让操作系统为您处理。你绝对不想一次下载一张图片,当一个下载完成后后台守护进程会在后台重新启动你的应用程序,这样你就可以启动下一个。这将是非常低效的(无论是在用户的电池方面还是在速度方面)。

    您需要在文件数组中循环,然后添加到会话以使其下载,但它将异步下载,因此也很难跟踪,因为文件很多。

    当然,如果请求是并行运行的,你不能简单地“添加到数组的末尾”,因为你不能保证它们完成的顺序。但是在这些响应进入时捕获它们并不难。例如,只需使用字典,可能由原始请求的 URL 键入。然后,您可以轻松地在该字典中查找与特定请求 URL 关联的响应。

    这非常简单。而且我们现在可以并行执行请求,速度更快,效率更高。

    你接着说:

    [并行下载]可能会导致电池高消耗,同时请求很多。这就是为什么我试图让它一次下载一个文件。

    不,您永远不需要为了电量而一次下载一个。如果有的话,一次下载一个会比较慢,而且会消耗更多的电量。


    不相关,如果您要下载 800 多个文件,您可能希望允许用户在用户处于“低数据模式”时不执行这些请求。例如,在 iOS 13 中,您可以设置 allowsExpensiveNetworkAccessallowsConstrainedNetworkAccess

    无论如何(特别是如果您支持较旧的 iOS 版本),您可能还需要考虑适当的设置 isDiscretionaryallowsCellularAccess

    归根结底,您要确保尊重用户有限的蜂窝数据计划,或者他们是否使用某些昂贵的服务(例如,使用飞机昂贵的数据计划连接或通过某些本地热点连接)。

    有关这些注意事项的更多信息,请参阅WWDC 2019 Advances in Networking, Part 1

    【讨论】:

    • 感谢您的澄清。实际上,我的情况是当用户想要下载最多 800files+ 的所有文件时。这就是为什么我不能让它们在前台下载或者只是在后台延长一些额外的时间。正如我在另一个问题中看到的那样,使用操作队列非常干净,因为我目前的技术是使用递归函数来完成任务。我想知道是否有另一种正确的方法来处理这个
    • 如果你的应用没有运行(后台URLSession就是这种情况),操作队列没有意义。如果您对递归方法没问题,请使用它(但请确保您将状态保存在持久存储中,以备不时之需,因为您的应用程序可能已被终止)。或者只是所有的请求,让后台URLSession 守护进程为你处理。但是操作队列是不可能的。
    • 谢谢你的建议,罗布。但是你能告诉我我应该用什么方法来处理这种情况,因为上一个项目我使用了这种风格,但有时它会遇到一个意想不到的问题。在IOS开发中。最困难的部分是当我需要使用与 android 开发完全相反的背景时
    • 就像我说的,为什么不一次将所有要下载的文件添加到会话中,然后放手呢?
    • 据我了解,这有一些缺点。您需要在文件数组中循环,然后添加到会话中以使其下载,但它将异步下载,因此也很难跟踪,因为文件很多。它可能会导致电池在大量请求的同时出现高消耗。这就是为什么我试图让它一次下载一个文件。
    猜你喜欢
    • 2014-05-07
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多