【问题标题】:How can I properly extend the background time of an iOS app so that an HTTP request doesn't get cancelled?如何正确延长 iOS 应用程序的后台时间,以免 HTTP 请求被取消?
【发布时间】:2021-01-27 23:13:44
【问题描述】:

我有一个应用程序 (App1),它使用 WKWebView 作为 UI 的很大一部分。有一种情况是,从WKWebView 向后端服务器发送 HTTP PUT 请求以保存一些数据。要完成此保存操作,服务器将需要通过另一个应用程序 (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;
}

【问题讨论】:

  • 嗨,你看看this official sample吗?
  • @JuniorJiang-MSFT 谢谢你的链接。我看了看,大部分都是有道理的。在调用EndBackgroundTask 之前它没有取消任务有什么原因吗?对我来说这似乎是合乎逻辑的,因为文档说要调用 EndBackgroundTask 来表明该任务的后台处理已经结束。
  • 你好,为什么它会在调用EndBackgroundTask之前取消任务?如果你调用EndBackgroundTask,那么它会取消任务。
  • @JuniorJiang-MSFT 我说的是这里创建的任务:Task.Factory.StartNew(() => FinishLongRunningTask(taskID));
  • 好吧,我已经更新了一个答案来解释这一点。

标签: c# ios xamarin xamarin.ios task-parallel-library


【解决方案1】:

以下代码 sn -p 演示了注册任务以在后台运行:

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 方法期间执行任务,这些任务必须在单独的线程上调用。因此,示例项目使用Task调用FinishLongRunningTask

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

【讨论】:

  • 对不起,如果我不清楚,但我已经明白了这一切。我在评论中的问题具体是关于是否对任务使用CancellationTokenSource,然后在调用EndBackgroundTask 之前取消它。看看我问题中的代码,你就会明白我在说什么。
  • @CShark Okey,很抱歉误解了您的疑问。如果要管理Task,最好使用CancellationTokenSource,在调用EndBackgroundTask之前取消。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
相关资源
最近更新 更多