清单 1. 使用 AFHTTPRequestOperation 实现断点续传的代码
// 1 指定下载文件地址 URLString
// 2 获取保存的文件路径 filePath
// 3 创建 NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]];
unsigned long long downloadedBytes = 0;
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// 3.1 若之前下载过 , 则在 HTTP 请求头部加入 Range
// 获取已下载文件的 size
downloadedBytes = [self fileSizeForPath:filePath];
// 验证是否下载过文件
if (downloadedBytes > 0) {
// 若下载过 , 断点续传的时候修改 HTTP 头部部分的 Range
NSMutableURLRequest *mutableURLRequest = [request mutableCopy];
NSString *requestRange =
[NSString stringWithFormat:@"bytes=%llu-", downloadedBytes];
[mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"];
request = mutableURLRequest;
}
}
// 4 创建 AFHTTPRequestOperation
AFHTTPRequestOperation *operation
= [[AFHTTPRequestOperation alloc] initWithRequest:request];
// 5 设置操作输出流 , 保存在第 2 步的文件中
operation.outputStream = [NSOutputStream
outputStreamToFileAtPath:filePath append:YES];
// 6 设置下载进度处理 block
[operation setDownloadProgressBlock:^(NSUInteger bytesRead,
long long totalBytesRead, long long totalBytesExpectedToRead) {
// bytesRead 当前读取的字节数
// totalBytesRead 读取的总字节数 , 包含断点续传之前的
// totalBytesExpectedToRead 文件总大小
}];
// 7 设置 success 和 failure 处理 block
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation
*operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
// 8 启动 operation
[operation start];
使用以上代码 , 断点续传功能就实现了,应用重新启动或者出现异常情况下 , 都可以基于已经下载的部分开始继续下载。关键的地方就是把已经下载的数据持久化。接下来简单看下 AFHTTPRequestOperation 是怎么实现的。通过查看源码 , 我们发现 AFHTTPRequestOperation 继承自 AFURLConnectionOperation , 而 AFURLConnectionOperation 实现了 NSURLConnectionDataDelegate 协议。
处理流程如图 4 所示:
图 4. AFURLHTTPrequestOperation 处理流程

这里 AFNetworking 为什么采取子线程调异步接口的方式 , 是因为直接在主线程调用异步接口 , 会有一个 Runloop 的问题。当主线程调用 [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES] 时 , 请求发出之后的监听任务会加入到主线程的 Runloop 中 ,RunloopMode 默认为 NSDefaultRunLoopMode, 这个表示只有当前线程的 Runloop 处理 NSDefaultRunLoopMode 时,这个任务才会被执行。而当用户在滚动 TableView 和 ScrollView 的时候,主线程的 Runloop 处于 NSEventTrackingRunLoop 模式下,就不会执行 NSDefaultRunLoopMode 的任务。










