iOS进入后台后长时间传输多个文件…

最近项目需要,需要支持用户下载多个文件时,即使用户将程序切换到后台后,仍然可以继续下载。
由于iOS在app切换到后台后只有有限的时间供app执行代码,之后程序代码会被suspend。
因 此利用application beginBackgroundTaskWithE xpirationHandler来延长后台app执行时间。(我是在iOS7环境下开发测试的,查看 [application backgroundTimeRemaining] == 180秒),但是超过这个时间后程序依然会被suspend。
在 网上搜索了一番,了解到iOS系统提供的后台多任务运行机制,包括伪后台和真后台,显然真后台的方式能满足需求。其中audio、voip、 location等都因担心app store审核不通过等原因被舍弃。当看到iOS7推出的background transfer service时眼前一亮,貌似这个可以满足需求。于是有了下面的一段代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/// AdTransmissionManager.m
 
//获取sessionManager
- (AFHTTPSessionManager*)sessionManager
{
     if (!_sessionManager) {
         NSURLSessionConfiguration *configuration = [ NSURLSessionConfiguration backgroundSessionConfiguration: self .upload ? background_identifier_upload : background_identifier_download];
         configuration.networkServiceType = NSURLNetworkServiceTypeBackground ;
         _sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
         _sessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
         //设置最大并发数1
         _sessionManager.operationQueue.maxConcurrentOperationCount = 1;
         __weak AdTransmissionManager* _weakSelf = self ;
         [_sessionManager setDidFinishEventsForBackgroundURLSessionBlock:^( NSURLSession *session) {
             //此处的completionHandler在handleEventsForBackgroundURLSession中赋值
             if (_weakSelf.completionHandler) {
                 _weakSelf.completionHandler();
                 _weakSelf.completionHandler = nil ;
             }
         }];
     }
     return _sessionManager;
}
 
//用户切换到后台时,前台任务转为后台任务执行
- ( void )enterBackground
{
     //获取正在运行的前台任务
     NSArray * tasks = [ self getRunningTasks];
     self .backgroundTasks = [ NSMutableArray array];
 
     for (AdTransmissionOperation* task in tasks) {
         //前台任务转为后台任务
         AdBackgroundTransmissionOperation* backgroundTask = [[AdBackgroundTransmissionOperation alloc] initWithTask:task];
         backgroundTask.sessionManager = self .sessionManager;
         backgroundTask.delegate = self ;
         [ self .backgroundTasks addObject:backgroundTask];
     }
 
     //进入后台
     self .InBackground = YES ;
 
     //启动第一个后台NSURLSessionTask
     [ self runBackground];
}
 
- ( void )runBackground
{
     if (! self .InBackground) {
         return ;
     }
 
     dispatch_queue_t dispatchQueue = _transmissionQueue;
     if (!dispatchQueue) {
         return ;
     }
 
     dispatch_async(dispatchQueue, ^{
         while ( self .backgroundTasks.count != 0 && self .InBackground) {
             //文件分块传输,每一个文件后台传输任务都被分割成多个NSURLSessionTask
             AdBackgroundTransmissionOperation* backgroundTask = [ self .backgroundTasks firstObject];
             //当AdBackgroundTransmissionOperation分块任务NSURLSessionTask传输失败或传输完成时,调用nextTask返回nil,此时需进入下一个AdBackgroundTransmissionOperation任务
             NSURLSessionTask * sessionTask = [backgroundTask nextTask];
             if (sessionTask) {
                 [sessionTask resume];
                 break ;
             }
             //移除当前AdBackgroundTransmissionOperation,进入下一个AdBackgroundTransmissionOperation
             [ self .backgroundTasks removeObjectAtIndex:0];
         }
     });
}
 
//用户进入app时调用,暂停后台任务,并将后台任务转为前台任务运行
- ( void )enterForeground
{
     self .InBackground = NO ;
     [ self .sessionManager.operationQueue cancelAllOperations];
     dispatch_queue_t dispatchQueue = _transmissionQueue;
     if (!dispatchQueue) {
         return ;
     }
     dispatch_async(dispatchQueue, ^{    
         AdBackgroundTransmissionOperation* backgroundTask = [ self .backgroundTasks firstObject];
         if (backgroundTask.task.info.status == TSRunning) {
             backgroundTask.task.info.status = TSSuspend;
         }
     });
 
     ...
 
}
 
 
/// AdBackgroundTransmissionOperation.m
 
// 获取下一个NSURLSessionTask,
- ( NSURLSessionTask *)nextTask
{
     NSURLSessionTask * sessionTask = nil ;
     sessionTask = [ self .sessionManager downloadTaskWithRequest:request progress: nil destination:^ NSURL *( NSURL *targetPath, NSURLResponse *response) {
             NSString * path = ...;
             return [ NSURL fileURLWithPath:path];
         } completionHandler:^( NSURLResponse *response, NSURL *filePath, NSError *error) {
             //do something
             ...
 
             //在上一个NSURLSessionTask完成时继续后面的NSURLSessionTask    
             //call runBackground
         }];
     return sessionTask;
}
 
 
/// AppDelegate.m
 
- ( void )application:(UIApplication *)application handleEventsForBackgroundURLSession:( NSString *)identifier
   completionHandler:( void (^)())completionHandler
{
     NSLog (@\\"handleEventsForBackgroundURLSession %@\\", identifier);
     [AdTransmissionManager setCompletionHandler:completionHandler ForURLSession:identifier];
}


使用上面的代码后,程序已经能在后台一直运行。但是每一个NSURLSessionTask开始执行到执行完的时间很长,需要大概60秒的时间。下面是我调试输出日志:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2014-06-13 12:33:38.936 XXXXX[2599:6c0f] onTransmissionStart:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg
 
……省略前台传输任务日志内容
 
=====  大约180秒后 程序suspend(进入Background transfer service 状态) =====
 
//分块 230 开始传输
2014-06-13 12:36:36.575 XXXXX[2599:6507] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:230, filesize:333619828
2014-06-13 12:36:36.984 XXXXX[2599:70b] handleEventsForBackgroundURLSession com.XXXXX.XXXXX-BackgroundDownload
//分块 230 传输完成
2014-06-13 12:36:36.998 XXXXX[2599:70b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:230, dataSize:1048576, newOffset:242221056
 
//分块 231 开始传输
2014-06-13 12:36:36.999 XXXXX[2599:f03] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:231, filesize:333619828
2014-06-13 12:37:37.001 XXXXX[2599:70b] handleEventsForBackgroundURLSession com.XXXXX.XXXXX-BackgroundDownload
//分块 231 传输完成
2014-06-13 12:37:37.019 XXXXX[2599:70b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:231, dataSize:1048576, newOffset:243269632
 
//分块 232 开始传输
2014-06-13 12:37:37.020 XXXXX[2599:790b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:232, filesize:333619828
2014-06-13 12:38:37.509 XXXXX[2599:70b] handleEventsForBackgroundURLSession com.XXXXX.XXXXX-BackgroundDownload
//分块 232 传输完成
2014-06-13 12:38:37.526 XXXXX[2599:70b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:232, dataSize:1048576, newOffset:244318208
 
//分块 233 开始传输
2014-06-13 12:38:37.527 XXXXX[2599:731b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:233, filesize:333619828
2014-06-13 12:41:37.715 XXXXX[2599:70b] handleEventsForBackgroundURLSession com.XXXXX.XXXXX-BackgroundDownload
//分块 233 传输完成
2014-06-13 12:41:37.739 XXXXX[2599:70b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:233, dataSize:1048576, newOffset:245366784
 
//分块 234 开始传输
2014-06-13 12:41:37.741 XXXXX[2599:12f0f] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:234, filesize:333619828
2014-06-13 12:46:37.878 XXXXX[2599:70b] handleEventsForBackgroundURLSession com.XXXXX.XXXXX-BackgroundDownload
//分块 234 传输完成
2014-06-13 12:46:37.893 XXXXX[2599:70b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:234, dataSize:1048576, newOffset:246415360
 
//分块 235 开始传输
2014-06-13 12:46:37.894 XXXXX[2599:1350f] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:235, filesize:333619828
2014-06-13 12:57:38.213 XXXXX[2599:70b] handleEventsForBackgroundURLSession com.XXXXX.XXXXX-BackgroundDownload
//分块 235 传输完成
2014-06-13 12:57:38.234 XXXXX[2599:70b] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:235, dataSize:1048576, newOffset:247463936
 
//分块 236 开始传输
2014-06-13 12:57:38.235 XXXXX[2599:6d2f] download data in background, filename:com.apple.adc.documentation.AppleiOS7.1.iOSLibrary.dmg, index:236, filesize:333619828
 
//现在时间2014-06-13 13:15:xx,第236块数据还没有传输完成



请问各位:
1. 我使用Background transfer service正确吗?
2. 为什么真后台运行后,系统接管NSURLSessionTask传输会这么慢?
3. 第236块数据怎么这么长时间还没有传完?
4. 系统接管NSURLSessionTask的机制是怎样的?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子木潇雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值