ODR使用异常:中断下载后beginAccessingResources无法二次完成
解决ODR下载中断后
beginAccessingResources无响应的问题 这个ODR的坑我之前踩过!当下载被后台中断(比如按Home键)后,系统的资源请求状态很容易卡住,导致后续调用beginAccessingResources永远得不到回调,磁盘状态一直显示Downloading其实是因为之前的请求没有被正确清理。
问题根源
当NSBundleResourceRequest的下载进程被后台中断时,系统并没有自动重置该请求的状态,它会一直处于NSBundleResourceRequestStateDownloading状态,但实际下载已经停止。此时再次调用beginAccessingResources,系统会认为已有一个活跃的下载请求,不会触发新的回调逻辑。
解决方案
1. 主动管理请求生命周期,后台时取消活跃请求
在应用进入后台时,主动取消当前的资源请求,并清理引用,避免状态残留:
// 声明成员变量持有当前请求 @property (nonatomic, strong) NSBundleResourceRequest *currentResourceRequest; // 应用进入后台的回调(在AppDelegate或SceneDelegate中) - (void)applicationDidEnterBackground:(UIApplication *)application { if (self.currentResourceRequest) { // 取消当前请求 [self.currentResourceRequest cancel]; // 清理引用 self.currentResourceRequest = nil; } }
2. 监听下载进度,处理停滞情况
如果请求在前台也出现进度停滞的情况,可以通过KVO监听progress属性,当长时间无进度更新时,主动取消并重新发起请求:
- (void)startResourceRequestWithTag:(NSString *)tag { self.currentResourceRequest = [[NSBundleResourceRequest alloc] initWithTags:@[tag]]; [self.currentResourceRequest conditionallyBeginAccessingResourcesWithCompletionHandler:^(BOOL resourcesAvailable) { if (resourcesAvailable) { NSLog(@"<!> Resource is available."); self.currentResourceRequest = nil; } else { NSLog(@"<!> Resource is NOT available."); // 添加进度监听 [self.currentResourceRequest.progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; [self.currentResourceRequest beginAccessingResourcesWithCompletionHandler:^(NSError * __nullable error) { // 移除监听 [self.currentResourceRequest.progress removeObserver:self forKeyPath:@"fractionCompleted"]; if (error) { NSLog(@"Download failed: %@", error.localizedDescription); self.currentResourceRequest = nil; // 可选:重新发起请求 [self startResourceRequestWithTag:tag]; } else { NSLog(@"Download completed!"); self.currentResourceRequest = nil; } }]; } }]; } // KVO回调处理进度停滞 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"fractionCompleted"]) { NSProgress *progress = (NSProgress *)object; static NSTimeInterval lastUpdateTime; static double lastProgressValue; NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970]; // 检查5秒内是否无进度更新 if (currentTime - lastUpdateTime > 5 && progress.fractionCompleted == lastProgressValue) { // 进度停滞,取消请求并重新发起 [self.currentResourceRequest cancel]; [self.currentResourceRequest.progress removeObserver:self forKeyPath:@"fractionCompleted"]; self.currentResourceRequest = nil; [self startResourceRequestWithTag:yourTag]; // 替换为你的资源标签 } lastUpdateTime = currentTime; lastProgressValue = progress.fractionCompleted; } }
3. 检查请求状态,重置卡住的请求
在发起新请求前,检查是否有遗留的活跃请求,如果有则先取消:
- (void)prepareResourceRequest { // 如果存在未完成的请求,先取消 if (self.currentResourceRequest && self.currentResourceRequest.state == NSBundleResourceRequestStateDownloading) { [self.currentResourceRequest cancel]; self.currentResourceRequest = nil; } // 发起新请求 [self startResourceRequestWithTag:yourTag]; }
总结
核心思路是避免请求状态残留,通过主动管理请求的生命周期(后台取消、停滞时重置),确保每次发起的beginAccessingResources都是基于一个全新的、状态干净的NSBundleResourceRequest实例,这样就能解决中断后无响应的问题。
内容的提问来源于stack exchange,提问作者Simon Lam
相关产品推荐
相关产品推荐

