iOS 应用进入后台时下载数据
Downloading data when iOS app goes in background
我有一个应用程序在第一次安装时会下载大量数据(主要是图像和文档文件)。目前,我只能在 HUD 中显示进度。但我希望我能以某种方式允许在应用程序进入后台(或设备被锁定)时下载数据。由于该应用程序的目标设备是 运行 iOS 7.0 及更高版本,因此我正在使用 NSURLSession
下载数据。我已经浏览了 Whosebug 上的各种主题以及教程 here。即使按照教程对我的应用程序进行了更改,我的应用程序也不会继续下载。我在 iPad 上测试了它。当应用程序被发送到后台(或锁定)时,下载暂停并在应用程序进入前台时恢复。
我不确定我的方法是错误的还是我的实现有缺陷。欢迎任何 help/advice。
The flow of the app is as follows: 'LoginViewController' calls an internal method downloadData
which has an object of the SaveProjectData
class that performs the task of downloading.
LoginViewController.m
@implementation LoginViewController
- (void)viewDidLoad {
}
- (IBAction)sumitButtonDidClick:(id)sender {
if ([self checkNetworkConnection] ==NotReachable) {
UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"Network Error" message:@"No internet Connection." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alertView show];
} else {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeDeterminateHorizontalBar;
hud.label.text = NSLocalizedString(@"Downloading...", @"HUD loading title");
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
if ([self loginStatus]) {
NSString * userName = self.user_nameTxtField.text;
CallProjectAPI * callProjectAPIinst = [[CallProjectAPI alloc] init];
NSString * appStatus = [[NSUserDefaults standardUserDefaults] objectForKey:@"appStatus"];
if ([appStatus isEqualToString:@"N"]) {
[callProjectAPIinst dataFromJSONFile];
[callProjectAPIinst saveImageName];
} else {
[self doSomeWorkWithProgress];
[callProjectAPIinst callAPI];
[self doSomeWorkWithProgress];
[self downloadData];
}
hud.label.text = NSLocalizedString(@"Complete!", @"HUD completed title");
porjectVC = [self.storyboard instantiateViewControllerWithIdentifier:@"ProjectViewController"];
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self presentViewController:porjectVC animated:YES completion:nil];
[hud hideAnimated:YES];
});
[[NSUserDefaults standardUserDefaults] setObject:userName forKey:@"userName"];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[hud hideAnimated:YES];
UIAlertView * alert=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"User Name or Password is wrong." delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil];
[alert show];
});
}
});
}
}
-(void)downloadData{
if ([self checkNetworkConnection] ==NotReachable) {
UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"Network Error" message:@"No internet Connection." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alertView show];
} else {
NSString * status =[[NSUserDefaults standardUserDefaults] objectForKey:@"DownloadData"];
if (status == nil) {
SaveProjectData * saveProjectDataInst = [[SaveProjectData alloc] init];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveAboutImage];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveConstructionUpdateImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveLogImages];
[saveProjectDataInst saveSmallImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveFloorPlanImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveUnitPlanImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveMasterPlanImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveBrochurs];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveGalleryImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveAmenitiesImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveVideos];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveMapImage];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveBannerImage];
[self doSomeWorkWithProgress];
[saveProjectDataInst savewalkthrough];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveFlatImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveEventImages];
[self doSomeWorkWithProgress];
[[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DownloadData"];
[saveProjectDataInst getUpdatedListForEachProject];
}
}
}
- (void)doSomeWorkWithProgress {
progress += 0.049f;
dispatch_async(dispatch_get_main_queue(), ^{
// Instead we could have also passed a reference to the HUD
// to the HUD to myProgressTask as a method parameter.
[MBProgressHUD HUDForView:self.view].progress = progress;
});
}
@end
Another approach could be to provide a button that the user clicks and data is downloaded while the user can still continue to use the
device for other perposes. Any pointers as to how I can implement
it?
iirc 你不应该依赖于当你的应用程序在后台下载东西,因为你不知道它什么时候会由于内存压力或其他原因而被杀死
这是有关它的 Apple 文档
@HimanshuMahajan here 给出了这个问题(类似的)的答案
我正在添加该答案并发布一个对我有用的解决方案,并在 iPad 上成功测试了它。
1) 在 ViewController
的头文件中使用以下行
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
2) 在 ViewDidLoad 中分配 UIBackgroundTaskIdentifier,如:
self.backgroundTask = UIBackgroundTaskInvalid;
3) 使用下面的代码行,这里我只是在 beginBackgroundTaskWithExpirationHandler:
块
中保持 getDataFromServer 方法
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
/* Here your downloading Code, let say getDataFromServer method */
[self getDataFromServer]; // Its dummy method
/* Your downloading Code End Here */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
});
4) 如果你想检查后台下载数据的剩余时间,在applicationDidEnterBackground中包含以下行:(UIApplication *)AppDelegate的应用委托方法:
NSLog(@"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining);
Adding to the answer:
5) 在applicationDidEnterBackground:(UIApplication *)application
方法中添加如下代码,允许后台执行无时间限制
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
希望有人认为答案有用!
我有一个应用程序在第一次安装时会下载大量数据(主要是图像和文档文件)。目前,我只能在 HUD 中显示进度。但我希望我能以某种方式允许在应用程序进入后台(或设备被锁定)时下载数据。由于该应用程序的目标设备是 运行 iOS 7.0 及更高版本,因此我正在使用 NSURLSession
下载数据。我已经浏览了 Whosebug 上的各种主题以及教程 here。即使按照教程对我的应用程序进行了更改,我的应用程序也不会继续下载。我在 iPad 上测试了它。当应用程序被发送到后台(或锁定)时,下载暂停并在应用程序进入前台时恢复。
我不确定我的方法是错误的还是我的实现有缺陷。欢迎任何 help/advice。
The flow of the app is as follows: 'LoginViewController' calls an internal method
downloadData
which has an object of theSaveProjectData
class that performs the task of downloading.
LoginViewController.m
@implementation LoginViewController
- (void)viewDidLoad {
}
- (IBAction)sumitButtonDidClick:(id)sender {
if ([self checkNetworkConnection] ==NotReachable) {
UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"Network Error" message:@"No internet Connection." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alertView show];
} else {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeDeterminateHorizontalBar;
hud.label.text = NSLocalizedString(@"Downloading...", @"HUD loading title");
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
if ([self loginStatus]) {
NSString * userName = self.user_nameTxtField.text;
CallProjectAPI * callProjectAPIinst = [[CallProjectAPI alloc] init];
NSString * appStatus = [[NSUserDefaults standardUserDefaults] objectForKey:@"appStatus"];
if ([appStatus isEqualToString:@"N"]) {
[callProjectAPIinst dataFromJSONFile];
[callProjectAPIinst saveImageName];
} else {
[self doSomeWorkWithProgress];
[callProjectAPIinst callAPI];
[self doSomeWorkWithProgress];
[self downloadData];
}
hud.label.text = NSLocalizedString(@"Complete!", @"HUD completed title");
porjectVC = [self.storyboard instantiateViewControllerWithIdentifier:@"ProjectViewController"];
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self presentViewController:porjectVC animated:YES completion:nil];
[hud hideAnimated:YES];
});
[[NSUserDefaults standardUserDefaults] setObject:userName forKey:@"userName"];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[hud hideAnimated:YES];
UIAlertView * alert=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"User Name or Password is wrong." delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil];
[alert show];
});
}
});
}
}
-(void)downloadData{
if ([self checkNetworkConnection] ==NotReachable) {
UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"Network Error" message:@"No internet Connection." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alertView show];
} else {
NSString * status =[[NSUserDefaults standardUserDefaults] objectForKey:@"DownloadData"];
if (status == nil) {
SaveProjectData * saveProjectDataInst = [[SaveProjectData alloc] init];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveAboutImage];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveConstructionUpdateImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveLogImages];
[saveProjectDataInst saveSmallImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveFloorPlanImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveUnitPlanImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveMasterPlanImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveBrochurs];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveGalleryImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveAmenitiesImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveVideos];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveMapImage];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveBannerImage];
[self doSomeWorkWithProgress];
[saveProjectDataInst savewalkthrough];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveFlatImages];
[self doSomeWorkWithProgress];
[saveProjectDataInst saveEventImages];
[self doSomeWorkWithProgress];
[[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DownloadData"];
[saveProjectDataInst getUpdatedListForEachProject];
}
}
}
- (void)doSomeWorkWithProgress {
progress += 0.049f;
dispatch_async(dispatch_get_main_queue(), ^{
// Instead we could have also passed a reference to the HUD
// to the HUD to myProgressTask as a method parameter.
[MBProgressHUD HUDForView:self.view].progress = progress;
});
}
@end
Another approach could be to provide a button that the user clicks and data is downloaded while the user can still continue to use the device for other perposes. Any pointers as to how I can implement it?
iirc 你不应该依赖于当你的应用程序在后台下载东西,因为你不知道它什么时候会由于内存压力或其他原因而被杀死
这是有关它的 Apple 文档
@HimanshuMahajan here 给出了这个问题(类似的)的答案 我正在添加该答案并发布一个对我有用的解决方案,并在 iPad 上成功测试了它。
1) 在 ViewController
的头文件中使用以下行@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
2) 在 ViewDidLoad 中分配 UIBackgroundTaskIdentifier,如:
self.backgroundTask = UIBackgroundTaskInvalid;
3) 使用下面的代码行,这里我只是在 beginBackgroundTaskWithExpirationHandler:
块
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
/* Here your downloading Code, let say getDataFromServer method */
[self getDataFromServer]; // Its dummy method
/* Your downloading Code End Here */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
});
4) 如果你想检查后台下载数据的剩余时间,在applicationDidEnterBackground中包含以下行:(UIApplication *)AppDelegate的应用委托方法:
NSLog(@"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining);
Adding to the answer:
5) 在applicationDidEnterBackground:(UIApplication *)application
方法中添加如下代码,允许后台执行无时间限制
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
希望有人认为答案有用!