【问题标题】:Downloading data when iOS app goes in backgroundiOS 应用程序进入后台时下载数据
【发布时间】:2018-07-10 05:21:29
【问题描述】:

我有一个应用程序,它在首次安装时会下载大量数据(主要是图像和文档文件)。目前,我只能在 HUD 中显示进度。但我希望我能以某种方式允许在应用程序进入后台(或设备被锁定)时下载数据。由于该应用程序针对的是运行 iOS 7.0 及更高版本的设备,因此我使用 NSURLSession 下载数据。我已经通过 Stackoverflow 上的各种线程以及教程here。即使按照教程对我的应用程序进行了更改,我的应用程序也不会继续下载。我在 iPad 上进行了测试。当应用程序被发送到后台(或锁定)时,下载会暂停并在应用程序进入前台时恢复。

我不确定我的方法是错误的还是我的实施有缺陷。欢迎任何帮助/建议。

应用程序的流程如下:'LoginViewController'调用内部方法downloadData,该方法具有执行下载任务的SaveProjectData类的对象。

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

另一种方法可能是提供一个按钮,用户单击该按钮并下载数据,同时用户仍然可以继续使用 其他人的设备。关于我如何实现的任何指针 是吗?

【问题讨论】:

  • 查看此 (stackoverflow.com/questions/45338412/…) 答案和示例项目。它可能会帮助你。
  • “我可以在应用程序进入后台时以某种方式允许下载数据”这是一个不合逻辑的请求。
  • 谢谢@beshio。
  • @ElTomato,请求背后的逻辑是“初始”数据下载量巨大且一次性。因此,用户可以将应用程序设置为下载数据,然后在设备上执行一些其他任务。希望你明白了。
  • 这个链接可以帮助你有哪些可能性:raywenderlich.com/143128/…

标签: ios objective-c background-process nsurlsession


【解决方案1】:

iirc 你不应该依赖在你的应用程序在后台下载东西,因为你不知道它什么时候会因为内存压力或其他原因而被杀死

这是关于它的 Apple 文档

https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html#//apple_ref/doc/uid/TP40007072-CH2-SW7

【讨论】:

  • 谢谢@johndoe。我之前已经浏览过文档,但客户要求以这样的方式配置应用程序,以便他/她将应用程序下载数据并离开设备一段时间,或在设备上执行一些其他活动.
【解决方案2】:

@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);

补充答案:

5)在applicationDidEnterBackground:(UIApplication *)application方法中添加如下代码,让后台执行无时间限制

UIApplication *app = [UIApplication sharedApplication];
    UIBackgroundTaskIdentifier bgTask;
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
    }];

希望有人能找到有用的答案!

【讨论】:

    猜你喜欢
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多