2018年12月05日 16:09:00 weixin_34101784 阅读数:5

https://blog.csdn.net/weixin_34101784/article/details/87569604

断点续传

demo

demog.gif

断点续传的原理是在HTTP1.1协议(RFC2616)中定义了断点续传相关的HTTP头的Range和Content-Range字段,支持只请求资源的一部分。

Range:可以请求文件资源的一个或者多个子范围。

例如:

  表示头500个字节:bytes=0-499;

  表示第二个500字节:bytes=500-999;

  表示最后500个字节:bytes=-500;

  表示500字节以后的范围:bytes=500- ; 

  第一个和最后一个字节:bytes=0-0,-1;

  同时指定几个范围:bytes=500-600,601-999;

Content-Range:字段说明服务器返回了文件的某个范围及文件的总长度。这时Content-Length字段就不是整个文件的大小了,而是对应文件这个范围的字节数,这一点一定要注意。一般格式,Content-Range: bytes 500-999/1000

NSURlSessionDownloadTask

iOS可以使用NSURlSessionDownloadTask来实现下载的断点续传功能,它提供了resumeData来实现断点续传功能,不需要在httpheader里设置Range了。网上关于NSURlSessionDownloadTask实现断点续传下载的代码有很多,这里总结下自己遇到的问题。

下载暂停和恢复

NSURlSessionDownloadTask有两种实现暂停的方法:

  • suspend:直接调suspend方法可以使task暂停下载,恢复下载可以调用resume方法,;
  • cancelByProducingResumeData::这个方法会取消task,从block里会得到一个resumeData。resumeData是用来恢复下载的。由于之前的task已经被取消了,方法downloadTaskWithResumeData:可以使用resumeData来获取一个新的NSURlSessionDownloadTask来继续下载。

在使用suspend时碰到的问题:

如果一个task正在下载,调用suspend暂停task后,然后com+R重启应用,虽然在session的getTasksWithCompletionHandler:的block里能够获取到task,但是重新resume的时候有时候会失败,有时候成功,不知道为什么。所以建议使用cancelByProducingResumeData来实现暂停功能。

把resumeData转成字符串打印看一下:

  • <?xml version="1.0" encoding="UTF-8"?>
  • <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  • <plist version="1.0">
  • <dict>
  • <key>NSURLSessionDownloadURL</key>
  • <string>http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg</string>
  • <key>NSURLSessionResumeBytesReceived</key>
  • <integer>8297040</integer>
  • <key>NSURLSessionResumeCurrentRequest</key>
  • <data>
  • ...
  • </data>
  • <key>NSURLSessionResumeInfoTempFileName</key>
  • <string>CFNetworkDownload_sFZ0E8.tmp</string>
  • <key>NSURLSessionResumeInfoVersion</key>
  • <integer>4</integer>
  • <key>NSURLSessionResumeOriginalRequest</key>
  • <data>
  • ...
  • </data>
  • <key>NSURLSessionResumeServerDownloadDate</key>
  • <string>Thu, 12 May 2016 02:41:27 GMT</string>
  • </dict>
  • </plist>

可以看出,resumeData实质是一个plist文件,里面包含了一些下载的信息,NSURLSessionResumeBytesReceived对应的是已经下载的字节数,NSURLSessionResumeInfoTempFileName对应的是下载的临时文件的名字。

如果下载出现错误,文档里是这样描述的

When any task completes, the NSURLSession object calls the delegate’s URLSession:task:didCompleteWithError: method with either an error object or nil (if the task completed successfully). If the download task can be resumed, the NSError object’s userInfo dictionary contains a value for the NSURLSessionDownloadTaskResumeData key. Your app should pass this value to call downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: to create a new download task that continues the existing download. If the task can’t be resumed, your app should create a new download task and restart the transaction from the beginning. In either case, if the transfer failed for any reason other than a server error, go to step 3 (creating and resuming task objects).

出错时会调用URLSession:task:didCompleteWithError:获得error对象,如果下载是可以恢复的,可以使用error.userInfo[NSURLSessionDownloadTaskResumeData]来获取resumeData恢复下载。有两种情况获取resumeData:

  • task调用cancelByProducingResumeData:之后会调用URLSession:task:didCompleteWithError:来获取resumeData,
  • 还有user主动kill应用后系统会取消下载中的task,重新启动时创建session后也可以在URLSession:task:didCompleteWithError:里获取到resumeData。

NSURLSessionDownloadTask在后台下载

NSURLSessionDownloadTask是支持后台下载的。

downloading_files_in_the_background

Downloading Content in the Background

When downloading files, apps should use an NSURLSession object to start the downloads so that the system can take control of the download process in case the app is suspended or terminated. When you configure an NSURLSession object for background transfers, the system manages those transfers in a separate process and reports status back to your app in the usual way. If your app is terminated while transfers are ongoing, the system continues the transfers in the background and launches your app (as appropriate) when the transfers finish or when one or more tasks need your app’s attention.

Once configured, your NSURLSession object seamlessly hands off upload and download tasks to the system at appropriate times. If tasks finish while your app is still running (either in the foreground or the background), the session object notifies its delegate in the usual way. If tasks have not yet finished and the system terminates your app, the system automatically continues managing the tasks in the background. If the user terminates your app, the system cancels any pending tasks.

文档里提到了app的几种情况:

  • 如果app在后台,但是is still running(比如按home键把app切到后台),会正常调用session的代理方法;
  • 如果app被系统terminate了(比如app在后台时间过长可能会被系统强制杀掉),系统会继续在后台管理session的task,当task完成时系统会启动app,此时只要使用相同的id创建session就会回调代理方法了;
  • 如果使用户主动kill了app,系统会取消session的task,重新启动时,用相同的id创建session,会调用代理方法URLSession:task:didCompleteWithError:,可以获取到resumeData来恢复下载。

在后台下载完成时的处理

下载任务在后台完成后:

如果app在后台,但是没有被系统teminate,系统会resume 应用并且调用UIApplicationDelegate的代理方法application:handleEventsForBackgroundURLSession:completionHandler:。之后会调用session的代理方法。

如果app在后台时候被系统terminate了,当下载task完成时,系统会在后台重启应用并调用UIApplicationDelegate的代理方法application:handleEventsForBackgroundURLSession:completionHandler:。方法里获得的identifier是之前创建session时的identifier。需要用这个idetifier重新创建session,之后会调用session的代理方法。

调用这个方法获得的completionHandler可以让系统知道您的应用程序的用户界面已更新,并且可以拍摄新的快照。一般在session的代理方法URLSessionDidFinishEventsForBackgroundURLSession:中调用。

在下载过程中用户主动kill app

用户主动kill app会导致下载task取消,当应用再次启动时,需要使用之前创建session的identifier重新创建session,之后会调用URLSession: task: didCompleteWithError:方法,从error中可以获取resumeData来恢复下载。

系统终止了app,重启时获取下载中的task

比如应用退到后台,因为内存问题被系统teminate,这时候下载任务不会取消,系统会继续管理下载task,此时若重新打开应用,可以使用相同的identifier创建session,然后通过

  • [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
  • }];

这个方法来获取task。如果在模拟器上正在进行下载任务,然后com+R重新运行程序也可以使用这个方法获取之前创建的下载task。

如果task在下载过程中用户主动kill app,task会被取消,再重新启动应用时。上面的这个方法会获取到取消的task,它的state是NSURLSessionTaskStateCompleted,不能使用这个task继续请求了,可以从session的代理方法里获取resumeData重新创建task来继续请求。

iOS 大文件下载、断点续传、后台下载 —— HERO博客

https://blog.csdn.net/hero_wqb/article/details/80407478

2018年05月23日 14:40:02 hero_wqb 阅读数:6681

版权声明:转载请注明出处。 https://blog.csdn.net/hero_wqb/article/details/80407478

本篇简述一下实现文件下载功能,包含大文件下载,后台下载,杀死进程,重新启动时继续下载,设置下载并发数,监听网络改变等,并在最后附有Demo。

下载功能的实现:

使用的网络连接的类为NSURLSession。该类用以替代NSURLConnection,在iOS7时推出,至此iOS系统才有了后台传输。在初始化NSURLSession前,需要先创建NSURLSessionConfiguration,可以理解为是NSURLSession需要的一个配置。NSURLSessionConfiguration有三种模式:

1. default:可以使用缓存的Cache、Cookie、鉴权。

2. ephemeral,仅内存缓存,不使用缓存的Cache、Cookie、鉴权。

3. background,支持后台传输,需要一个identifier标识,用来重新连接session对象。

创建后台模式NSURLSessionConfiguration:

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"HWDownloadBackgroundSessionIdentifier"];

创建NSURLSession,设置配信息、代理、代理线程:

NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[[NSOperationQueue alloc] init]];

在实现下载前,还需要了解一个很重要的类,NSURLSessionTask,无论下载多少文件,我们只需要初始化一个NSURLSession即可,而每个task对应一个任务,需要通过task才能实现下载,NSURLSessionTask是一个基类,有四个子类:

1. NSURLSessionDataTask:下载时,内容以NSData对象返回,需要我们不断写入文件,但不支持后台传输,切换后台会终止下载,回到前台时在协议方法中输出error,下面贴一下用NSURLSessionDataTask实现断点续传的核心代码:

  • // 遵守协议
  • <NSURLSessionDataDelegate>
  • // 创建NSMutableURLRequest
  • NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
  • [request setValue:[NSString stringWithFormat:@"bytes=%zd-", tmpFileSize] forHTTPHeaderField:@"Range"];
  • // 创建NSURLSessionDataTask
  • NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
  • // 开始、继续下载
  • [dataTask resume];
  • // 暂停下载
  • [dataTask suspend];
  • // 取消下载
  • [dataTask cancel];
  • // 接收到服务器响应
  • - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
  • {
  • // 更新文件的总大小
  • totalFileSize = response.expectedContentLength + tmpFileSize;
  • // 创建输出流
  • NSOutputStream *stream = [[NSOutputStream alloc] initToFileAtPath:fullPath append:YES];
  • [stream open];
  • // 允许处理服务器的响应,继续接收数据
  • completionHandler(NSURLSessionResponseAllow);
  • }
  • // 接收到服务器返回数据,会被调用多次
  • - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
  • {
  • // 写入数据
  • [stream write:data.bytes maxLength:data.length];
  • // 当前下载大小
  • tmpFileSize += data.length;
  • // 进度
  • self.progressView.progress = 1.0 * tmpFileSize / totalFileSize;
  • }
  • // 当请求完成之后调用,如果错误,那么error有值
  • - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
  • {
  • [stream close];
  • }
  • - (void)dealloc
  • {
  • [session invalidateAndCancel];
  • }

有几点需要注意,调用cancel方法会立即进入-URLSession: task: didCompleteWithError这个回调;调用suspend方法,即使任务已经暂停,但达到超时时长,也会进入这个回调,可以通过error进行判断;当一个任务调用了resume方法,但还未开始接受数据,这时调用suspend方法是无效的。也可以通过cancel方法实现暂停,只是每次需要重新创建NSURLSessionDataTask。

2. NSURLSessionUploadTask:继承自NSURLSessionDataTask,内容以NSData对象返回,协议方法中可以查看请求时上传内容的过程,支持后台传输。

3. NSURLSessionStreamTask:建立了一个TCP/IP连接,替代NSInputStream/NSOutputStream,新的API可异步读写,自动通过HTTP代理连接远程服务器。

4. NSURLSessionDownloadTask:笔者推荐使用该task实现文件下载,断点续传系统帮我们做了,资源会下载到一个临时文件,下载完成需将文件移动至想要的路径,系统会删除临时路劲文件,暂停时,系统会返回NSData对象,恢复下载时用这个data创建task,支持后台传输,下面重点介绍一下NSURLSessionDownloadTask的使用:

创建NSURLSessionDownloadTask,有两种方式,后面会讲解NSData在哪里获取,其中需要注意一点,在iOS 10.0和iOS 10.1系统中,使用downloadTaskWithResumeData:会发生数据错误问题,需要进行额外处理,具体可以在Demo中查看:

  • // 根据NSData对象创建,可以继续上次进度下载
  • NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithResumeData:resumeData];
  • // 根据NSURLRequesta对象创建,开启新的下载
  • NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:model.url]]];

开始、继续下载用NSURLSessionTask的resume方法,暂停下载用下面方法,这里拿到回调的NSData,保存,可以通过它来创建task实现继续下载:

  • [downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
  • model.resumeData = resumeData;
  • }];

遵守协议,实现相应协议方法:

NSURLSessionDownloadDelegate:

  • /**
  •  接收到服务器返回数据,会被调用多次,可获取文件大小,进度,计算速度等
  •  @param bytesWritten 当次写入文件大小
  •  @param totalBytesWritten 已写入文件大小
  •  @param totalBytesExpectedToWrite 文件总大小
  •  */
  • - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
  • {
  • // 计算进度
  • model.progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
  • }
  • // 下载完成
  • - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
  • {
  • // 移动文件,原路径文件由系统自动删除
  • [[NSFileManager defaultManager] moveItemAtPath:[location path] toPath:localPath error:nil];
  • }

NSURLSessionTaskDelegate,注意调用cancel、cancelByProducingResumeData:方法也会调用:

  • // 请求完成,有错误时,error有值
  • - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;

后台下载:

到这里,已经可以通过NSURLSessionDownloadTask实现断点续传了,下面介绍如何实现后台下载,其实非常简单,一共三步:

1. 创建NSURLSession时,需要创建后台模式NSURLSessionConfiguration,上面已经介绍过了。

2. 在AppDelegate中实现下面方法,并定义变量保存completionHandler代码块:

  • // 应用处于后台,所有下载任务完成调用
  • - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
  • {
  • _backgroundSessionCompletionHandler = completionHandler;
  • }

3. 在下载类中实现下面NSURLSessionDelegate协议方法,其实就是先执行完task的协议,保存数据、刷新界面之后再执行在AppDelegate中保存的代码块:

  • // 应用处于后台,所有下载任务完成及NSURLSession协议调用之后调用
  • - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
  • {
  • dispatch_async(dispatch_get_main_queue(), ^{
  • AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  • if (appDelegate.backgroundSessionCompletionHandler) {
  • void (^completionHandler)(void) = appDelegate.backgroundSessionCompletionHandler;
  • appDelegate.backgroundSessionCompletionHandler = nil;
  • // 执行block,系统后台生成快照,释放阻止应用挂起的断言
  • completionHandler();
  • }
  • });
  • }

程序终止,再次启动继续下载:

后台下载实现之后,再看一下如何实现进程杀死后,再次启动时继续下载,在应用程序被杀掉时,系统会自动保存应用下载session信息,重新启动应用时,如果创建和之前相同identifier的session,系统会找到对应的session数据,并响应-URLSession: task: didCompleteWithError:方法,打印error输出如下:

error: Error Domain=NSURLErrorDomain Code=-999 "(null)" UserInfo={NSURLErrorBackgroundTaskCancelledReasonKey=0, NSErrorFailingURLStringKey=https://www.apple.com/105/media/cn/iphone-x/2017/01df5b43-28e4-4848-bf20-490c34a926a7/films/feature/iphone-x-feature-cn-20170912_1280x720h.mp4, NSErrorFailingURLKey=https://www.apple.com/105/media/cn/iphone-x/2017/01df5b43-28e4-4848-bf20-490c34a926a7/films/feature/iphone-x-feature-cn-20170912_1280x720h.mp4, NSURLSessionDownloadTaskResumeData=<CFData 0x7ff401097c00 [0x104cb6bb0]>{length = 6176, capacity = 6176, bytes = 0x3c3f786d6c2076657273696f6e3d2231 ... 2f706c6973743e0a}}

可以看到,有几点有用的信息:

1)error.localizedDescription为"(null)",打印结果为"The operation couldn't be completed. (NSURLErrorDomain error -999.)"。

2)[error.userInfo objectForkey:NSURLErrorBackgroundTaskCancelledReasonKey]有值。

3)返回了NSURLSessionDownloadTaskResumeData。

综上进程杀死后,再次启动继续下载的思路就是,重启时,创建相同identifier的session,在-URLSession: task: didCompleteWithError:方法中拿到resumeData,用resumeData创建task,就可以恢复下载。

再说明一下,另外一种不可取的思路,在appDelegate中进程杀死时会调用-applicationWillTerminate:方法,在这里task调用cancelByProducingResumeData:方法暂停正在下载的任务,但是这个方法的回调需要时间,还没有执行到代码块进程就已经终止了。

并发数设置:

下面介绍一下下载并发数的设置:NSURLSession本身就支持多任务同时下载,它会根据性能内部控制同时下载的个数,最多5个。一个任务对应一个NSURLSessionDownloadTask,所以想多任务同时下载,需要创建多个task,可以用数组或字典保存。我们定义变量去记录当前下载文件个数及用户设置的最大下载个数。

监听网络改变:用AFN监听,可以点击这里查看

为了增加用户体验,往往在设置中会给用户一个选项, 选择蜂窝网络下是否允许下载。NSURLSessionConfiguration本身就有一个属性allowsCellularAccess,默认为YES,允许蜂窝网络下载。如果不需要用户随时变更这个选项,是可以用这个属性。但是对于正在下载的任务,修改这个属性是无效的,即我们已经通过session创建了task对象,开启了任务,再试图用session.configuration.allowsCellularAccess = NO;去修改这个选项是无效的。如果一定要用这个属性修改这个选项,那么只能重新创建session:

  • // 重新创建后台NSURLSessionConfiguration,并且identifier需要改变,不能与之前一样
  • NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"HWDownloadBackgroundSessionIdentifierNew"];
  • // 修改是否允许蜂窝网络下载
  • configuration.allowsCellularAccess = NO;
  • // 重新创建NSURLSession
  • _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[[NSOperationQueue alloc] init]];

所以我们创建NSURLSessionConfiguration时把allowsCellularAccess设为YES,然后定义一个变量去控制是否允许蜂窝网络下载,在网络状态改变及用户设置修改这个选项之后,调用暂停、开启任务。

数据保存:用FMDB存储数据,可以点击这里查看

下载速度计算:

声明两个变量,一个记录时间,一个记录在特定时间内接收到的数据大小,在接收服务器返回数据的-URLSession: downloadTask: didWriteData: totalBytesWritten: totalBytesExpectedToWrite:方法中,统计接收到数据的大小,达到时间限定时,计算速度=数据/时间,然后清空变量,为方便数据库存储,这里用的时间戳:

  • - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
  • {
  • // 记录在特定时间内接收到的数据大小
  • model.intervalFileSize += bytesWritten;
  • // 获取上次计算时间与当前时间间隔
  • NSInteger intervals = [[NSDate date] timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:model.lastSpeedTime * 0.001 * 0.001]];
  • if (intervals >= 1) {
  • // 计算速度
  • model.speed = model.intervalFileSize / intervals;
  • // 重置变量
  • model.intervalFileSize = 0;
  • model.lastSpeedTime = [[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970] * 1000 * 1000] integerValue];
  • }
  • }

Demo效果图:

这里在模型中加入了一个变量,记录任务加入准备下载的时间,用于计算任务开始的先后顺序,如上图3,开启任务08、09、10、11、12,暂停,依次开启10、11、12、08、09,然后将最大并发数由5改为2,暂停的应该为12、08、09三个任务,当10下载完成,开启的应该是12而不是08。

Demo下载链接:https://github.com/HeroWqb/HWDownloadDemo

写博客的初心是希望大家共同交流成长,博主水平有限难免有偏颇之处,欢迎批评指正。

转载于:https://www.cnblogs.com/sundaysgarden/p/10524095.html

ios 后台下载,断点续传总结相关推荐

  1. iOS 后台下载及管理库

    说起下载第一个想起的就是ASI.一年前接手的新项目是核心功能是视频相关业务,在修改和解决视频下载相关的问题的时候让我体会到了ASI的下载的强大.后来新需求需要视频后台下载,使用NSURLSession ...

  2. iOS后台下载功能(收集)

    在iOS7以前的系统中,App默认是不能后台运行的,如果要后台运行,可以采用以下两类方法: (1)使用beginBackgroundTaskWithExpirationHandler函数,向系统申请一 ...

  3. iOS 原生级别后台下载详解

    怎样才能并发地下载一堆文件,并且能够在后台全部下载完成后再执行其他操作? 当然,这个问题其实很简单,解决方案也有很多.但我第一时间想到的是,目前是否存一个非常权威,非常流行.稳定可靠,并且是用 Swi ...

  4. iOS项目技术还债之路《一》后台下载趟坑

    前言 去年底我在公司开始接手几个迭代了五六年的iOS老项目的技术优化工作.互联网公司的闭源N手业务老代码,经过了若干年和若干波人的轮番洗礼,再加上若干个deadline的赶工加持,已经是千疮百孔,改点 ...

  5. 基于iOS 10封装的下载器(支持存储读取、断点续传、后台下载、杀死APP重启后的断点续传等功能)

    原文 资源来自:http://www.cocoachina.com/ios/20170316/18901.html 概要 在决定自己封装一个下载器前,我本以为没有那么复杂,可在实际开发过程中困难重重, ...

  6. 使用 NSURLSession 开发一个支持后台下载和断点续传的下载工具

    NSURLSession 是 iOS 系统提供给我们的原生网络操作库,它提供了网络操作相关的一系列特性支持,比如缓存控制,Cookie管理,HTTP 认证处理等等,是一套整体的网络操作处理解决方案. ...

  7. linux后台断点下载到指定目录,Linux下wget 断点续传 及 后台下载

    几个比较高频的命令参数:51Testing软件测试网5uM(jI"?~;r"~r1kC 51Testing软件测试网+B)iSS-Z"F)I 4DQ~1L8^h0 使用w ...

  8. 猫猫学Swift之下载-断点续传

    猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://write.blog.csdn.net/postlist 下载-断点续传 通过URLSession进行下载,通过O ...

  9. IOS后台运行机制详解(一)

    一.iOS的"伪后台"程序 首先,先了解一下iOS 中所谓的「后台进程」到底是怎么回事吧? Let me be as clear as I can be: the iOS mult ...

最新文章

  1. Python使用matplotlib可视化发散型点图、发散型点图可以同时处理负值和正值、并按照大小排序区分数据、为发散型点图添加数值标签(Diverging Dot Plot )
  2. KafKa集群安装、配置
  3. 3.3. shutdown
  4. MySQL Workbench/SQLyog 高分辨率屏幕上界面模糊的解决方法
  5. Spring AOP源码解析(三)—— AOP引入(续)
  6. vue实现原理解析及一步步实现vue框架
  7. lombok和maven_Lombok,AutoValue和Immutables,或如何编写更少,更好的代码返回
  8. HomeBrew 安装 国内源
  9. 【kafka】Unexpected handshake request with client mechanism GSSAPI, enabled mechanisms are
  10. oracle 字符串 回车,如何去掉字符串中的回车键
  11. JavaScript创建与读写本地文件(IEFirefox)
  12. android:AIDL
  13. 【Tensorflow/keras】KeyError: ‘loss‘
  14. Unity 外部资源无法拖入Unity
  15. linux如何查看隐藏进程中勒索病毒,.FileFuck勒索病毒删除+数据恢复(HiddenTear变体)...
  16. yum软件包管理 yum(软件仓库)
  17. 最强模拟器Genymotion到期开始收费破解教程
  18. java 给word加水印,Java 实现在线给word 文档添加水印
  19. 【OpenGL】平面法向量、顶点法向量的计算方法
  20. struts2+hibernate留言板并发布到自己的主机上(-)

热门文章

  1. usart和uart 的区别
  2. bzoj1670【Usaco2006 Oct】Building the Moat 护城河的挖掘
  3. java XML解析防止外部实体注入
  4. 44.Android之Shape设置虚线、圆角和渐变学习
  5. javascript设计模式-继承
  6. (线段树 点更新 区间求和)lightoj1112
  7. Android Studio 插件
  8. Sql Server字符串函数
  9. 推荐一个配置linux服务的网站
  10. 软件包管理 之 fedora-rpmdevtools 工具介绍