Xamarin.iOS中HTTP错误后是否需新建NSURLSessionTask实现上传重试?
在Xamarin.iOS后台上传中处理HTTP错误重试的正确姿势
首先回答你的核心问题:每次遇到可重试的HTTP错误时新建NSURLSessionTask是完全正确的实现方式——因为NSURLSessionTask一旦完成(成功或失败)就无法重新启动,必须创建新的任务来发起重试请求。不过这里有几个关键细节需要注意,避免踩坑:
一、先明确哪些4xx/5xx错误值得重试
不是所有错误都适合重试,得先做过滤:
- 应该重试的:408(请求超时)、429(请求限流)、5xx系列(服务器内部错误,大概率是临时问题)
- 不该重试的:401(未授权)、403(禁止访问)、404(资源不存在)——这类错误重试也没用,反而浪费资源
先在DidCompleteWithError里细化判断逻辑:
public async override void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error) { if (task.Response is NSHttpUrlResponse httpResponse) { if (httpResponse.StatusCode == 200) { // 成功:清理本地存储的待上传数据 RemoveBookingFromPendingUploads(task.TaskDescription); } else if (IsRetryableStatusCode(httpResponse.StatusCode)) { // 触发重试逻辑 await RetryUploadForBooking(task.TaskDescription); } else { // 非重试类错误:记录日志,标记任务失败 var errorMsg = $"Upload failed with non-retryable status code: {httpResponse.StatusCode}"; Crashes.TrackError(new ApplicationException(errorMsg)); MarkBookingAsFailed(task.TaskDescription); } } else if (error != null) { // 网络类错误(比如无网、连接超时),同样触发重试 await RetryUploadForBooking(task.TaskDescription); } } // 辅助方法:判断是否为可重试状态码 private bool IsRetryableStatusCode(int statusCode) { return statusCode == 408 || statusCode == 429 || (statusCode >= 500 && statusCode < 600); }
二、持久化待上传数据,避免重试时丢失信息
后台Session可能被系统回收,或者App被临时杀死,所以必须把BookingDTO、文件路径、重试次数这些关键信息持久化到本地(比如用Core Data、NSUserDefaults或本地文件)。比如首次上传前先保存:
public void UploadFile(BookingDTO newBookingDTO) { // 1. 先把待上传的Booking信息持久化到本地 SaveBookingToPendingUploads(newBookingDTO); // 2. 创建请求和上传任务(你的原有逻辑) var request = CreateUploadRequest(newBookingDTO); // 替换成你的请求创建代码 var uploadTask = session.CreateUploadTask(request, NSUrl.FromFilename(newBookingDTO.FilePath)); uploadTask.TaskDescription = newBookingDTO.ClientId; uploadTask.Resume(); }
三、实现带次数限制的重试逻辑
一定要加重试次数上限,避免无限循环给服务器和App造成压力,同时搭配指数退避策略(每次重试等待时间翻倍),减少对服务器的冲击:
private async Task RetryUploadForBooking(string clientId) { // 1. 从本地获取对应的Booking信息 var booking = GetBookingFromPendingUploads(clientId); if (booking == null) return; // 2. 检查重试次数是否超过上限(比如最多3次) if (booking.RetryCount >= 3) { MarkBookingAsFailed(clientId); Crashes.TrackError(new ApplicationException($"Upload failed after 3 retries for booking {clientId}")); return; } // 3. 指数退避等待:1s → 2s → 4s... var delaySeconds = Math.Pow(2, booking.RetryCount); await Task.Delay(TimeSpan.FromSeconds(delaySeconds)); // 4. 更新重试次数并重新保存 booking.RetryCount++; UpdateBookingInPendingUploads(booking); // 5. 发起新的上传任务 UploadFile(booking); }
四、后台Session的关键注意事项
- 必须使用同一个后台Session标识符(
com.uba.BookingUploadBackgroundSession)创建新任务,否则系统无法在后台统一管理这些上传任务。 - 后台Session的任务会由iOS系统接管,即使App被挂起或杀死,系统也会继续完成上传(或重试),完成后会唤醒App调用
DidCompleteWithError。
额外最佳实践
- 监听网络状态:用
Reachability类监听网络变化,在网络恢复时再触发重试,避免无网时的无效尝试。 - 文件存在性检查:重试前先确认待上传文件是否还存在,避免因文件丢失导致的报错。
- 详细日志:记录每次重试的时间、状态码、重试次数,方便后续排查问题。
内容的提问来源于stack exchange,提问作者empo
相关产品推荐
相关产品推荐

