如何正确延长 iOS 应用程序的后台时间,以免 HTTP 请求被取消?

问题描述

我有一个应用程序 (App1),它在 UI 的很大一部分中使用了 WKWebView。有一个场景,HTTP PUT 请求从 WKWebView 发送到后端服务器以保存一些数据。要完成此保存操作,服务器将需要通过另一个应用程序 (App2) 的批准。用户通常会切换到 App2 进行批准,然后切换回 App1 以查看保存结果。问题是当 App1 进入后台时,它会导致对保存请求的响应被取消,即使在后端服务器上保存完全成功。没有实际记录任何错误,但我相当肯定它正在发生,因为当应用程序在后台挂起时,iOS 正在终止连接。我的想法基于 this discussion

由于批准在 App2 上保存所需的时间并不长,我想我可以尝试延长 App1 的后台时间,它似乎在我测试过的时间有效。

>

但是,我想知道这是否真的是最佳策略,如果是,是否对我的代码有任何建议(例如,我是否应该将 BeginBackgroundTask 移到 Task.Run 内) :

我以这些 microsoft docs 为例。

public override async void DidEnterBackground(UIApplication application)
{
    ExtendBackgroundTime(application);
}

private nint? webViewBgTaskId = null;
private CancellationTokenSource webViewBgTaskTokenSrc = null;

private void ExtendBackgroundTime(UIApplication application)
{
    // cancel the prevIoUs background task that was created in this function
    webViewBgTaskTokenSrc?.Cancel();
    webViewBgTaskTokenSrc = null;
    if (webViewBgTaskId.HasValue)
    {
        application.EndBackgroundTask(webViewBgTaskId.Value);
        webViewBgTaskId = null;
    }

    var cts = new CancellationTokenSource();
    nint taskId = default;
    taskId = application.BeginBackgroundTask(() =>
    {
        cts.Cancel();
        webViewBgTaskTokenSrc = null;
        application.EndBackgroundTask(taskId);
        webViewBgTaskId = null;
    });

    _ = Task.Run(async () =>
    {
        // For Now,this is just set to 5 minutes,but in my experience,// the background task will never be allowed to continue for that long.
        // It's usually only about 30 seconds as of iOS 13.
        // But this at least gives it some finite upper bound.
        await Task.Delay(TimeSpan.FromMinutes(5),cts.Token);

        application.EndBackgroundTask(taskId);
        webViewBgTaskId = null;
    },cts.Token);

    webViewBgTaskTokenSrc = cts;
    webViewBgTaskId = taskId;
}

解决方法

以下代码片段演示了注册要在后台运行的任务:

nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});

//runs on main or background thread
FinishLongRunningTask(taskID);

UIApplication.SharedApplication.EndBackgroundTask(taskID);

注册过程将任务与唯一标识符 taskID 配对,然后将其包装在匹配的 BeginBackgroundTaskEndBackgroundTask 调用中。为了生成标识符,我们调用 UIApplication 对象上的 BeginBackgroundTask 方法,然后启动长时间运行的任务,通常是在一个新线程上。当任务完成时,我们调用 EndBackgroundTask 并传入相同的标识符。这很重要,因为如果 BeginBackgroundTask 调用没有匹配的 EndBackgroundTask,iOS 将终止应用程序。

注意:如果您想在 DidEnterBackground 方法中执行 Tasks,这些任务必须在单独的线程上调用。因此,示例项目使用 Task 来调用 FinishLongRunningTask

Task.Factory.StartNew(() => FinishLongRunningTask(taskID));