11

我需要在后台和前台完成一项长时间运行的任务。这会更新核心数据。因此,为了保持 UI 响应,我创建了另一个线程,在其中使用不同的 managedObjectContext(MOC)。因此,在后台和前台都设置了一个计时器,并在状态更改时适当地停用。在任务开始之前和任务完成之后,当我按下主页按钮时,它会正确调用两个委托方法,但是当我按下主页按钮屏幕更改和 UI 挂起(变为空白)时任务处于活动状态但两个委托方法不是正确调用并且应用程序不会终止。我找不到发生这种情况的原因。如果有人可以提供帮助,那将很有帮助。

我将附上所需的代码:

-(void) startTimerThread
{
    dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        // Add code here to do background processing
        NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
        [context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]];
        self.managedObjectContext = context;
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(mergeChanges:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:context];
        NSLog(@"managedObjContext : %@\n",self.managedObjectContext);
        [self getDataFromFile];

        dispatch_async( dispatch_get_main_queue(), ^{
            // Add code here to update the UI/send notifications based on the
            // results of the background processing

            [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadAppDelegateTable" object:nil];
            [context release];
            [[NSNotificationCenter defaultCenter] removeObserver:self 
                                                            name:NSManagedObjectContextDidSaveNotification
                                                          object:context];
        });
    });
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    NSLog(@"Background\n");
    [self.notificationTimer invalidate];
    self.notificationTimer = nil;
    UIApplication  *app = [UIApplication sharedApplication];
    self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask]; 
        bgTask = UIBackgroundTaskInvalid;
    }];

    //start location update timer and background timer 
    self.timer = [NSTimer scheduledTimerWithTimeInterval:180 target:self
                                                selector:@selector(startLocationServices) userInfo:nil repeats:YES];
    self.locationManager.delegate = self; 
    [self.locationManager startUpdatingLocation]; 

    self.logDownloader.managedObjectContext = self.managedObjectContext;
    NSLog(@"managedObjContext : %@\n",self.logDownloader.managedObjectContext);
    self.backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:90 target:self.logDownloader selector:@selector(getDataFromFile) userInfo:nil repeats:YES];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    NSLog(@"Foreground\n");
    //invalidate background timer and location update timer
    [self.timer invalidate];
    [self.backgroundTimer invalidate];
    self.timer = nil;
    self.notificationTimer = nil;

    self.logDownloader.managedObjectContext = self.managedObjectContext;
    NSLog(@"managedObjContext : %@\n",self.logDownloader.managedObjectContext);
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadAppDelegateTable" object:nil];

    self.notificationTimer = [NSTimer scheduledTimerWithTimeInterval:180 target:self.logDownloader selector:@selector(startTimerThread) userInfo:nil repeats:YES];
}
4

4 回答 4

30

在 iOS13+ 上,如果您实现 UIWindowSceneDelegate,它会func sceneDidEnterBackground(_ scene: UIScene)改为调用 .

于 2020-04-22T17:47:35.677 回答
19

applicationDidEnterBackground:和永远不会被调用的原因applicationDidEnterForeground:是因为这些方法与Application does not run in background这个选项一起使用可以在你的***-info.plist. 如果此选项设置为,那么YES您的应用程序将永远不会调用这些方法,因为当您按下主页按钮时,如果您使用已将选项设置YES为正在运行的应用程序实例的应用程序,这些方法将被终止,因此每次您按下主页按钮时然后选择正在创建新实例的应用程序图标,因此它正在使用applicationWillTerminate:.

上面所说的方法Kirti mali也将是不正确的方法,用于想要你想要的,原因是applicationDidBecomeActive:applicationWillResignActive:你接听电话时会使用它。正在运行的实例不会被终止,也不会被发送到后台。该实例将暂停,直到用户完成该调用,然后它将再次变为活动状态。

因此,解决方案是,如果您希望应用程序在后台运行,则将选项“ Application does not run in background”更改***-info.plistNO公正applicationDidBecomeActive:,并且applicationWillResignActive:是使用这些方法的错误方式。

请参阅苹果文档UIApplicationDelegate以更好地了解这些方法。

于 2013-04-12T14:47:13.803 回答
4

由于您的应用程序在后台运行,因此永远不会调用这些方法,但是您可以使用willEnterForegroundNotificationdidEnterBackgroundNotification来做您想做的事情。

您可以在 ApplicationDelegate 的 didFinishLaunchingWithOptions 方法中编写以下代码

    NotificationCenter.default.addObserver(forName:UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { (_) in
        // Your Code here
}

NotificationCenter.default.addObserver(forName:UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { (_) in
       // Your Code here        
}
于 2020-05-10T09:01:10.303 回答
-3

按下主页按钮时调用此方法

- (void)applicationDidBecomeActive:(UIApplication *)application

并且在按下图标按钮时调用此方法

- (void)applicationWillResignActive:(UIApplication *)application
于 2013-03-14T09:36:30.473 回答