`

AFNetworking的success block跑在UI Thread里

 
阅读更多

我运行在子线程中的代码,调用了AFNetworking的API来下载文件:

// targetPath是下载的临时文件路径,:app_dir/tmp/CFNetworkDownload_9z499O.tmp
    NSURL* (^destinationBlock) (NSURL *targetPath, NSURLResponse *response) = ^NSURL* (NSURL *targetPath, NSURLResponse *response){
        return [NSURL fileURLWithPath:localFilePath];// 下载文件最终存放地址,不同于targetPath
    };
    
    // filePath即上面那个block的返回值
    void (^completionBlock) (NSURLResponse *response, NSURL *filePath, NSError *error) = ^void (NSURLResponse *response, NSURL *filePath, NSError *error){
        NSLog(@"download success, file written to: %@", [filePath path]);
        [[NSNotificationCenter defaultCenter] postNotificationName:RESUME_ZIP_FILE_DOWNLOAD_DONE object:self userInfo:nil];
    };
    
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:destinationBlock completionHandler:completionBlock];
    [downloadTask resume];

DEBUG才发现,completion block默认是跑在UI Thread里的。中间的过程有点复杂,其实下载确实是运行在子线程里,但是下载的文件只放在临时目录,还没移动到我指定的最终目录。所以下载刚完成,子线程后续的代码就开始跑了(都是基于下载文件已移动到最终目录的前提)。但是这个时候其实文件还不存在,要等completion block执行完,才会移动到最终目录

所以造成了一个BUG,在文件还不存在的时候,后续的逻辑就开始读取了。我的解决办法是,子线程调用下载API以后,就停止干活。completion block的最后发一个通知,原来发起调用的类,收到通知以后,再进行后续的操作:

// 创建目录,下载恢复文件
-(void) initResume
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    // 如果:enterpriseId/resume目录不存在,则创建
    NSString *resumeDir = [YLSGlobalUtils getResumeDirPath];
    if(![fileManager fileExistsAtPath:resumeDir]){
        [fileManager createDirectoryAtPath:resumeDir withIntermediateDirectories:YES attributes:nil error:nil];
    }
    
    // 下载恢复zip文件
    [YLSUploadHelper doDownload:[self resumeFilePath]];
}

// 响应恢复文件下载完成的事件
-(void) listenDownloadDown
{
    [self performSelectorInBackground:@selector(processAfterDownloadDone) withObject:nil];
}

-(void) processAfterDownloadDone
{
    // 各种逻辑
}

我感觉这种解决方式并不是很好,太依赖事件通知,系统里会有很多通知飞来飞去,而且代码也很啰嗦。后续还是需要用GCD方式来解决


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics