[英]How can I properly extend the background time of an iOS app so that an HTTP request doesn't get cancelled?
I have an app (App1) that makes use of the WKWebView
for a good portion of the UI.我有一个应用程序 (App1) 将WKWebView
用于大部分 UI。 There is a scenario where an HTTP PUT request is sent from the WKWebView
to a backend server to save some data.有这样一种场景,从WKWebView
向后端服务器发送 HTTP PUT 请求以保存一些数据。 For this save operation to complete, the server will need approval thru another app (App2).要完成此保存操作,服务器将需要通过另一个应用程序 (App2) 的批准。 The user would normally switch to App2 to approve, then switch back to App1 to see the result of the save.用户通常会切换到 App2 进行批准,然后切换回 App1 以查看保存结果。 The problem is that when App1 gets backgrounded, it can cause the response to the save request to be cancelled, even though the save was completely successful on the backend server.问题是,当 App1 进入后台时,它可能会导致对保存请求的响应被取消,即使在后端服务器上保存完全成功。 There isn't any errors actually logged, but I'm fairly certain it is happening because iOS is killing the connection when the app gets suspended after it gets backgrounded.实际上没有记录任何错误,但我相当肯定它正在发生,因为 iOS 在应用程序在后台运行后暂停时正在终止连接。 I'm basing my thoughts on this discussion .我的想法是基于这个讨论。
Since the time it takes to approve the save on App2 isn't that long, I figured I could just try to extend the background time of App1, and it appears to work in the times I've tested it.由于批准在 App2 上保存所需的时间并没有那么长,我想我可以尝试延长 App1 的后台时间,并且它似乎在我测试过的时间内有效。
However, I want to know if this is really the best strategy, and if so, are there any recommendations on my code (For example, should I move the BeginBackgroundTask
inside of the Task.Run
):但是,我想知道这是否真的是最好的策略,如果是,对我的代码是否有任何建议(例如,我是否应该将BeginBackgroundTask
Task.Run
):
I used these microsoft docs as an example.我以这些微软文档为例。
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;
}
The following code snippet demonstrates registering a task to run in the background:以下代码片段演示了注册要在后台运行的任务:
nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});
//runs on main or background thread
FinishLongRunningTask(taskID);
UIApplication.SharedApplication.EndBackgroundTask(taskID);
The registration process pairs a task with a unique identifier, taskID
, and then wraps it in matching BeginBackgroundTask
and EndBackgroundTask
calls.注册过程将任务与唯一标识符taskID
,然后将其包装在匹配的BeginBackgroundTask
和EndBackgroundTask
调用中。 To generate the identifier, we make a call to the BeginBackgroundTask method on the UIApplication
object, and then start the long-running task, usually on a new thread.为了生成标识符,我们在UIApplication
object 上调用 BeginBackgroundTask 方法,然后启动长时间运行的任务,通常在新线程上。 When the task is complete, we call EndBackgroundTask
and pass in the same identifier.任务完成后,我们调用EndBackgroundTask
并传入相同的标识符。 This is important because iOS will terminate the application if a BeginBackgroundTask
call does not have a matching EndBackgroundTask
.这很重要,因为如果BeginBackgroundTask
调用没有匹配的EndBackgroundTask
,iOS 将终止应用程序。
Note : If you want to perform Tasks During DidEnterBackground
method, these tasks must be invoked on a separate thread.注意:如果要在DidEnterBackground
方法期间执行任务,则必须在单独的线程上调用这些任务。 Therefore, sample project uses Task to invoke FinishLongRunningTask
.因此,示例项目使用 Task 来调用FinishLongRunningTask
。
Task.Factory.StartNew(() => FinishLongRunningTask(taskID));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.