谈一谈iOS开发后台下载
Posted JackLee18
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谈一谈iOS开发后台下载相关的知识,希望对你有一定的参考价值。
由于ios系统对于后台执行任务管控比较严格,如果app没有任务继续执行,那么app在进入后台一段时间后会被系统杀死。如果下载大文件的话,需要考虑如何在app进入后台后继续下载。
app保活策略
app保活是指app进入后台后,通过静默的重复执行某个后台允许的任务,保证App不被系统杀死。例如:后台播放没有声音的音频。app保活应用比较广泛,不仅仅可以实现后台下载大文件。其他的应用场景:比如用户通过一定路径进入某个比较重要的页面,但这个页面操作可能需要切换app等,为了让用户下载进入app的时候仍然停留在这个页面。那么需要通过开启保活策略,同时可以根据业务需求设置,一直进行保活,保活一段时间后,关闭掉保活策略。
使用系统提供的后台下载策略
使用系统提供了NSURLDownloadTask 可以实现后台下载大文件的需求。原理:app创建允许后台下载的session,并注册到操作系统中。此时下载的操作会交给操作系统处理。当文件下载好后,会返回下载好的默认路径。进入后台后,只要app不被用户主动杀死,那么这个注册的session里的下载任务会继续执行。等到session上的任务下载完成后,会唤醒app执行一些简单的操作。具体大家可以参考网址:《iOS 原生级别后台下载详解》
下载任务的创建需要经历几个阶段。
下载任务第一次创建
如果一个文件第一次创建下载需求,那么不需要考虑太多,直接创建downloadTask就好了。
离开创建下载页面后,再次进入下载页面(此时app未被系统杀死)
如果全局持有task,那么可以根据保存的task恢复原来的下载逻辑,以及进度信息,不用重新创建task。最大程度节省恢复下载的成本。由于我参考了YTKNetwork的实现逻辑(全局持有正在执行的request对象),因此恢复起来不需要直接比对task了。具体代码如下:
JKDownloadRequest *request = nil;
NSArray *allRequests = [[[JKNetworkAgent sharedAgent] allRequests] copy];
for (JKBaseRequest *baseRequest in allRequests) {
if ([baseRequest isKindOfClass:[JKDownloadRequest class]]) {
JKDownloadRequest *downloadRequest = (JKDownloadRequest *)baseRequest;
if ([downloadRequest.absoluteString isEqualToString:url]
&& downloadRequest.backgroundPolicy == backgroundPolicy
&& [downloadRequest.downloadedFilePath isEqualToString:downloadedPath]) {
request = downloadRequest;
}
}
}
app被系统杀死后,再次进入下载页面或者再次下载之前已经创建过的下载任务
当App重新启动时,之前全局保存的task,已经不存在了,需要调用相关方法重新从操作系统中获取上次正在进行的未完成的task,并全局持有。然后根据需要执行相应的恢复逻辑。具体代码如下:
NSURLSessionTask *task = [[JKNetworkAgent sharedAgent] lastExcutingBackgroundTaskOfURLString:url];// 通过和从操作系统获取到的app启动前正在执行的tasks,让后根据url匹配到到对应的task,执行响应的恢复逻辑。
if (task) {
request.requestTask = task;
request = [self initWithUrl:url];
request.downloadedFilePath = downloadedPath;
request.backgroundPolicy = backgroundPolicy;
request.isRecoveredFromSystem = YES;
}
对于后台任务的session上的task执行完以后,此时操作系统会唤醒app,我这边将逻辑进行了封装,大家可以监听如下的通知,然后在通知响应的方法中执行相应的操作。具体示例如下:
- (instancetype)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleBackgroundDownloadingTasksComplete:) name:JKBackgroundTaskCompleteAndInvokeAppNotification object:nil];
}
return self;
}
- (void)handleBackgroundDownloadingTasksComplete:(NSNotification *)notification
{
if (![notification.object isKindOfClass:[NSURLSessionDownloadTask class]]) {
return;
}
NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *)notification.object;
NSArray<JKDownloadFileModel *>* models = [JKDownloadFileModel allDownloadingFiles];
for (JKDownloadFileModel *model in models) {
NSString *urlString = task.originalRequest.URL.absoluteString;
if ([urlString isEqualToString:model.fileURLString]) {
[JKDownloadFileModel deleteObjectFromDownloading:model];
model.downloadedSize = model.fileSize;
[JKDownloadFileModel insertObjectToDownloaded:model];
}
}
}
备注:收到这个通知的时候,app并未正常执行didFinishLaunching逻辑,如果这里的逻辑需要用到一些正常启动时创建的对象,那么需要在这里提前创建好单独处理。
另外这个地方由于涉及到反复的启动,函数的执行逻辑判断需要多次的启动才可以判断。我这里通过打印日志的形式来实现的。这里用到了自己写的一个日志打印库JKLogHelper,可以很方便通过打印日志来调试程序。JKLogHelper源码
实现后台下载的源码地址:https://github.com/xindizhiyin2014/JKNetworking.git
更多干货文章,欢迎扫描二维码关注功能
以上是关于谈一谈iOS开发后台下载的主要内容,如果未能解决你的问题,请参考以下文章