Yes, I know if I wish my app to be responsive to users' multitasking actions, such as switch to another app, I should deal with
- (void)applicationWillResignActive:(UIApplication *)application
- (void)applicationDidBecomeActive:(UIApplication *)application
What if my app is doing a quite-long time consuming operation (like downloading a big file) and the user causes my app to enter the background? Will that operation automatically be suspended and resumed when the user comes back to my app?
What exactly will happen behind the scene when my app enters the background or resumes in the foreground?
What if when users let my app go to the background my app's execution is just in the middle of a method?
For e.g., my app is doing
for (int i = 1 to 10000K) {
do some calculation;
}
When i== 500K, user switches to another app. What happens to the for-loop in my app?
From the documentation:
Return from applicationDidEnterBackground(_:) as quickly as possible. Your implementation of this method has approximately five seconds to perform any tasks and return. If the method doesn’t return before time runs out, your app is terminated and purged from memory.
If you need additional time to perform any final tasks, request additional execution time from the system by calling beginBackgroundTask(expirationHandler:). Call beginBackgroundTask(expirationHandler:) as early as possible. Because the system needs time to process your request, there’s a chance that the system might suspend your app before that task assertion is granted. For example, don’t call beginBackgroundTask(expirationHandler:) at the very end of your applicationDidEnterBackground(_:) method and expect your app to continue running.
If the long-running operation you describe above is on the main thread and it takes longer than 5 seconds to finish after your application heads to the background, your application will be killed. The main thread will be blocked and you won't have a chance to return from -applicationDidEnterBackground: in time.
If your task is running on a background thread (and it really should be, if it's taking long to execute), that thread appears to be paused if the application returns from -applicationDidEnterBackground: (according to the discussion in this answer). It will be resumed when the application is brought back to the foreground.
However, in the latter case you should still be prepared for your application to be terminated at any time while it's in the background by cleaning things up on your way to the background.
If you are doing some operation which might consume time and you don't want to kill it then you can extend the time for your operation by executing in UIBackground Task i
{
UIBackgroundTaskIdentifier taskId = 0;
taskId = [application beginBackgroundTaskWithExpirationHandler:^{
taskId = UIBackgroundTaskInvalid;
}];
// Execute long process. This process will have 10 mins even if your app goes in background mode.
}
The block argument called "handler" is what will happen when the background task expire (10min).
Here is a link to the documentation
Like mentioned above, there are a few cases where your app runs in the background and apple can allow or deny depending on what you are doing.
https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
More importantly if you do fit into one of these categories your app refresh rate is determined by an apple algorithm that takes into consideration your app usage on that device vs other apps. If your app is used more often then it gets more background time allotted. This is just one variable but you get the idea that background time allocation varies app to app and not under your control.
Related
I want to count costumer time(like this 00:20:04). But background app can work only 3 min.
I have timer for it, and I can count with NSDate how many seconds app be close. But don't know how call this func.
So my question is: How app can understand when app in background and stop work, and how understand when return to app?
Short answer:
Save current time when user start counting and calibrate it in applicationWillEnterForeground: , in fact you can just calibrate it in your code of setting up timer.
Long answer:
You won't receive any notification if your app is in suspend mode and then terminated.
Apps must be prepared for termination to happen at any time and should not wait to save user data or perform other critical tasks. System-initiated termination is a normal part of an app’s life cycle. The system usually terminates apps so that it can reclaim memory and make room for other apps being launched by the user, but the system may also terminate apps that are misbehaving or not responding to events in a timely manner.
Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory. If an app is currently running in the background and not suspended, the system calls the applicationWillTerminate: of its app delegate prior to termination. The system does not call this method when the device reboots.
In addition to the system terminating your app, the user can terminate your app explicitly using the multitasking UI. User-initiated termination has the same effect as terminating a suspended app. The app’s process is killed and no notification is sent to the app.
However you can use these method of AppDelegate to get notified when your app is launched, entering background or foreground:
application:willFinishLaunchingWithOptions:—This method is your app’s first chance to execute code at launch time.
application:didFinishLaunchingWithOptions:—This method allows you to perform any final initialization before your app is displayed to the user.
applicationDidBecomeActive:—Lets your app know that it is about to become the foreground app. Use this method for any last minute preparation.
applicationWillResignActive:—Lets you know that your app is transitioning away from being the foreground app. Use this method to put your app into a quiescent state.
applicationDidEnterBackground:—Lets you know that your app is now running in the background and may be suspended at any time.
applicationWillEnterForeground:—Lets you know that your app is moving out of the background and back into the foreground, but that it is not yet active.
applicationWillTerminate:—Lets you know that your app is being terminated. This method is not called if your app is suspended.
read iOS app life cycle and UIApplicationDelegate Protocol Reference for more detail.
I've looked around at answers to questions regarding executing core data saves in the background, but none that I've found have directly addressed the following scenario:
I've set up core data so that when my app launches for the first time, core data is populated from a plist and then saved to the persistent store (all on a background thread). During this time, the app displays a "please wait" popup with an animating activity indicator. This seems to work nicely and generally the app will finish the population even if the user hits the home button during the population, pushing the app to the background. However, it is possible that the app could be completely terminated before this population finishes (I've successfully done this by hitting home, double-tapping home, and removing the app from the multi-tasking tray really really fast). In this scenario, the app quits without saving the data to the store. On the next launch of the app, the app recognizes that the store already exists, so it doesn't populate, thus leaving the user with a completely empty database. So the question(s) is/are:
Is there a way to completely remove the store on app termination if the population hasn't finished? I attempted to do this in -applicationWillTerminate: which didn't seem to properly recognize if the app was in the middle of populating.
Or, is there a better way to recognize when the store requires population? E.g., can I determine if the store is empty upon launch?
I would suggest this general approach to your problem:
at the end of the population phase, write a flag to your core data base or to your app defaults (NSUserDefaults);
(now, when the app is terminated before finishing the population phase, the flag will not be stored);
at startup, check for that flag; if it is there, you know that the population phase completed, otherwise you know something went wrong.
Of course, there is a slight chance that the app is terminated just after the population phase is done and before the flag is stored; aside from this event to be pretty unlikely, in any case, if that happened, it would only force your app to populate the data once again at the next launch, but will not be cause of any data consistency issue.
A more specific approach is base on the use of beginBackgroundTaskWithExpirationHandler:
Your app delegate’s applicationDidEnterBackground: method has approximately 5 seconds to finish any tasks and return. In practice, this method should return as quickly as possible. If the method does not return before time runs out, your app is killed and purged from memory. If you still need more time to perform tasks, call the beginBackgroundTaskWithExpirationHandler: method to request background execution time and then start any long-running tasks in a secondary thread. Regardless of whether you start any background tasks, the applicationDidEnterBackground: method must still exit within 5 seconds.
This will allow you to get more time to complete the population phase before the app is terminated in any "non-pathological" case (i.e., it will not work if the user kills the app like you did in your test, and will not be safe in case something goes wrong in the app itself and it crashes while the background thread is doing the population).
Hope this helps.
I've just been reading this post on notifications being sent to apps running in the background :
Not getting didReceiveMemoryWarning when app is in the background
My question is that since an app in the background will not act on a didRecieveMemoryWarning until it enters the foreground again, what is the recommended way to free up memory in your app if it is running in the background when the memory notification is sent - or is this not possible ?
In iOS 4.0 and later, - (void)applicationDidEnterBackground:(UIApplication *)application method is called instead of the applicationWillTerminate: method when the user quits an application that supports background execution.
You should use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
You should also disable updates to your application’s user interface and avoid using some types of shared system resources (such as the user’s contacts database). It is also imperative that you avoid using OpenGL ES in the background.
Your implementation of this method has approximately five seconds to perform any tasks and return. If you need additional time to perform any final tasks, you can request additional execution time from the system by calling beginBackgroundTaskWithExpirationHandler:. In practice, you should return from applicationDidEnterBackground: as quickly as possible. If the method does not return before time runs out your application is terminated and purged from memory.
If your app is running in the background (because it is, for example, a Voice over IP app that is allowed to run in the background), it will receive memory warning notifications in the same way as if it were running in the foreground, and you should deal with them accordingly.
However, if your app is suspended in the background, it won't receive memory warnings or other notifications. Your job is to free as much memory as possible before your app enters the background. Once you're in the background, you have no way to do anything anymore. The OS will decide whether to kill your app or not (without notifying you again) at its discretion.
When my application is moved into the background, I'd like to be able to detect when it is about to be terminated (for memory exhaustion or other reasons). Is there a way to do this?
In particular, is there a way to execute some code before the application is terminated when in the background?
You can do this in the -[<UIApplicationDelegate> applicationWillTerminate:] method of your application delegate, like this:
- (void)applicationWillTerminate:(UIApplication *)application {
[database save]; // or whatever you want to do
}
This will be executed whenever the app is about to be terminated, unless it crashes.
Your best bet is to do whatever cleanup needs to be done in your application (saving state or user data, etc.) as your application transitions into the background. If your application is suspended, you will not have a chance to perform any last code before it is terminated by the system.
From the iOS Application Programming Guide:
If your application is running (either
in the foreground or background) at
termination time, the system calls
your application delegate’s
applicationWillTerminate: method so
that you can perform any required
cleanup. You can use this method to
save user data or application state
information that you would use to
restore your application to its
current state on a subsequent launch.
Your method implementation has
approximately 5 seconds to perform any
tasks and return. If it does not
return in time, the application is
killed and removed from memory. The
applicationWillTerminate: method is
not called if your application is
currently suspended.
Even if you develop your application
using iOS SDK 4 and later, you must
still be prepared for your application
to be killed without any notification.
The user can kill applications
explicitly using the multitasking UI.
In addition, if memory becomes
constrained, the system might remove
applications from memory to make more
room. If your application is currently
suspended, the system kills your
application and removes it from memory
without any notice. However, if your
application is currently running in
the background state (in other words,
not suspended), the system calls the
applicationWillTerminate: method of
your application delegate. Your
application cannot request additional
background execution time from this
method.
It depends.
If you mean getting notification while your application is suspended in the background, there is no way to know; the applicationWillTerminate: method is not run if you're suspended. The recommended approach is to save any required state when you get the applicationWillEnterBackground: message, so that if you get killed in the background you're ready to start up again.
If you're actually in an "executing in the background" state, (which can happen briefly after exiting the app or if the app has requested temporary background execution time,) then applicationWillTerminate: will be called just like you'd expect.
In my iPhone app I would like to run several queries when the
application is in background.
I already use ASIHttpRequest to make the queries, that works fine but
now I try to find a way to trigger them in background.
In the app delegate, I have added a call to the method making the request:
[self getItemsFromServer]
getItemsFromServer runs an asynchronous request (on the simulator I
saw the log of this methods once I get back the application to the
foreground).
How can I use some kind of timer to have this method ran every 10
minutes (I just need to run it 4 or 5 times, not each 10 minutes until
it goes back to foreground :-) )?
thanks a lot,
Best Regards,
Luc
iOS4 allows your app to run for X amount of time, granted that iOS4 grants you the time you request. Check out: Completing a Long-Running Task in the Background.
Specifically,
Any time before it is suspended, an application can call the beginBackgroundTaskWithExpirationHandler: method to ask the system for extra time to complete some long-running task in the background. If the request is granted, and if the application goes into the background while the task is in progress, the system lets the application run for an additional amount of time instead of suspending it
You could probably use Task Finishing to do that. In iOS you can mark a thread as finishing and give it a specific time to live. This would let you do a few more calls to your web server.
Have a look at Executing Code in the Background
Actually, you are specially not allowed to make general HTTP calls while in background. The only apps that can be active in the background are those that play audio, do location or are running VOIP calls. I believe Apple's whole philosophy with background is that apps shouldn't be doing 'work' other than these limited cases because there are limited resources available. THe suggested way to work around this is to use (ugh) notifications or just do a refresh when your application wakes up. The doc that willcodejavaforfood references explains this.