- (void)applicationDidEnterBackground:(UIApplication *)application
{
for (int i =0; i<30; i++){
//add a local notification and schedule it
}
}
when app switch to background, these codes will freeze app in a while.
There's no document about UIApplication is thread safe or not.
After a long time test I found most time execute LocalNotification on background works well. but some times it just crash our app.
So it seems like all the class with 'UI' prefixed are not thread safe and you should never invoke there's methods on another thread.
And my solution is reduced the number of LocalNotification, it still freeze app in a bit, but we thing we can accept this little freeze.
By default, application processing freezes when the app goes to the background. The execution continues from the same statement where it left when the app comes back to the foreground. To execute code in the background, you have to surround it in the beginBackgroundTaskWithExpirationHandler block.
Have a look at the section Completing a Finite-Length Task in the Background at http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html.
Related
I have a a few threads that needs to run constantly or be locked at my disposal. When I lock the phone or swap applications the thread seems to halt until the application is back in focus.
I have a class Worker that is a sub class of NSThread. There is a method called start which is called by the firstViewController that creates the Worker object.
//method start
[self performSelectorInBackground:#selector(run) withObject:self];
What do I need to do to make my thread run all the time, rather than only running while in focus?
Thanks :)
When your app enters the background, all threads as suspended - unless you've configured your application to use multi-tasking, and your work is being done using the multi-tasking methods. This is detailed at Apple developer.
In short, you basically can't have a thread running constantly in the background on iOS if you want to be accepted in the App store, unless you're a navigation or VOIP application. You can have a thread continue to run for around 10 minutes after you enter the background, but that's it.
I have been working on an opencv project for iOS. I was given a simple project to start developing with that captured and displayed frames. I never payed much attention to how it worked until I started having memory issues and traced them back to the original project setup. I now plan to re-write the capturing/displaying code but I don't understand why it worked in the first place. There was a play/pause button which called the method
- (IBAction)play_pause:(id)sender
{
play = !play;
while(play)
{
if (_videoCapture && _videoCapture->grab())
{
(*_videoCapture) >> _display_frame;
//process frame
self.imageView.image = [UIImage imageWithCVMat:_display_frame];
}
}
}
play is just a global bool that signifies whether the application is playing or paused. The strange thing is that the processing should be taking place inside an infinite loop, there is no way out. play is never modified within the loop. In spite of that, when the application is running the play/pause button remains responsive and is capable of flipping the play bool and pausing the execution. Not only that, other bools (use_greyscale for instance) can be flipped by other buttons and their values change inside the loop. I would have expected the application to freeze and never even draw new frames to the screen. The application should stay trapped inside that function for most of its lifetime, unable to perform other tasks such as drawing and UIControl. It seems as though the only way this is possible is if the IBAction call is running on its own thread. I cannot find any evidence of threading in the source code. Could someone explain how apple handles threading in its UI? I was under the impression that there was one main runloop thread and that extra threads were not created automatically. If that is true, how can this behavior be explained?
side note-
What finally made me investigate this was that [UIImage imageWithCVMat:_display_frame] returns an auto-released object. Since all this takes place inside a loop, the objects could not be released without the execution being paused which was causing crashes.
The reason is worked is because the implementation of the cv::VideoCapture::grab() method runs the current run loop to pause the thread until it gets a frame.
When you launch your application, the main function executes a function named UIApplicationMain, which executes CFRunLoopRun. When CFRunLoopRun is executed on the main thread, it runs the main run loop, which is the run loop that processes all the UI events received from the system and refresh the user interface. For information on run loops, you may read Apple Threading Programming Guide.
So, when you execute an infinite loop, your code never returns to the run loop and the waiting events cannot be processed. But in your case, the grab method runs the run loop again with an expiration delay. So the run loop may process incoming events (which may invoke your code again) until the delay expires, then return to your code that will run the run loop again.
If you look at the callstack when you touch the button to pause, you will see this:
main function → run loop → event handling → your code → OpenCV → run loop → event handling → your code
The run loop is running inside itself, which is perfectly fine because run loops are reentrant. Scroll views actually use that behavior: When you scroll a UIScrollView, it runs the run loop again in a different mode in order to ignore some events until you end scrolling.
But I'm not sure the developers of OpenCV had this in mind when they wrote their code. So I think it would be better to load your frames in a background thread/queue.
You are correct, there is no "automatic threading" in iOS. Grand Central Dispatch (GCD) certainly makes threading much easier, but it does not happen automatically.
You can write some debug code and test for [NSThread isMainThread] in the while loop to see if play_pause is indeed being run on the main UI thread, which I suspect it is not.
I'm opening the database connection in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
and I'm closing on applicationWillTerminate
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
NSLog(#"Closing DB");
if(database) sqlite3_close(database);
}
But when I close (really close) the app on the simulator, I do not get the log "Closing DB" in the console. Is it normal?
What is the best place to close a sqlite3 connection in Objective-c?
(really close means
– Double tap the Home button to bring up the Multitasking bar
– Press and hold anywhere on the multitasking bar until the icons on it start to wiggle.
– While they are wiggling, each icon has a Minus sign symbol above it.
– Press the Minus symbol above any app to close it down.)
This is because your application didn't terminate. It went into background. Try adding your code to applicationDidEnterBackground
Try to avoid usage of -applicationWillTerminate: method because you have not enough time(!) to do your work correctly - maybe system is rebooting or shutting down. The best practice to save data and purge unused resources (if needed) is to put your code in -applicationDidEnterBackground: method.
From Apple's UIApplicationDelegate Protocol Reference:
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.
You should perform any tasks relating to adjusting your user interface
before this method exits but other tasks (such as saving state) should
be moved to a concurrent dispatch queue or secondary thread as needed.
Because it's likely any background tasks you start in
applicationDidEnterBackground: will not run until after that method
exits, you should request additional background execution time before
starting those tasks. In other words, first call
beginBackgroundTaskWithExpirationHandler: and then run the task on a
dispatch queue or secondary thread.
(c) taken from this answer
My goal is to run a repeating timer every five seconds if and only if the application is running in the background. I've tried a couple of ideas, but they don't seem to work.
Idea 1: Doesn't run even once.
- (void)applicationDidEnterBackground:(UIApplication *)application {
[NSTimer scheduledTimerWithTimeInterval:(5.0/5.0) target:self selector:#selector(check_expiry) userInfo:nil repeats:YES];
}
Idea 2: Runs every five seconds, but I can't seem to stop the loop.
- (void)applicationDidEnterBackground:(UIApplication *)application {
counter = YES;
while (counter) {
sleep(5);
[self check_expiry];
}
// Counter is set to NO in willEnterForeground and didBecomeActive, but this loop continues to run due the sleep();
}
How can I get this loop to run properly?
Thanks!
When an application "enters the background" in iOS, that's not like normal operating systems, where it continues to run. The application enters a suspended state. It doesn't keep running; only application state is preserved, and even that's not guaranteed - if the device is running low on memory, iOS will happily terminate your application to give the memory to the active application. If you attempt to block in applicationDidEnterBackground:, like you are doing with sleep(), iOS will simply terminate your application for not returning from that method promptly.
Your application is woken up periodically if it's configured for background processing GPS events, VOIP, etc., but abusing those just to get your app to run will stop you from getting App Store approval.
This is all covered in The iOS Application Programming Guide.
For anyone looking for a workaround, I merely created a system that schedules timers at a later date when the applicationDidEnterBackground: and changed/cancelled them when they were edited/deleted. Information on timer scheduling was stored in a local dictionary.
I'm using NSURLConnection to download resources asynchronously in iOS. (They are large-ish PDF files, so it takes some time on a slow connection.)
Now I'm updating my app from iOS 3 to iOS 4. As my app is none of location-aware, voip, and background music, I guess I need to do something.
My question is, then, what happens to the NSURLConnection currently running? Is it suspended and magically resumed when the app comes back to the foreground, or is it outright killed? If it is the latter, what is the standard strategy to resume it automatically later? Is there a open-source subclass of NSURLConnection which automatically does that?
You can start a task that will run for at most 10 minutes. Look at using the beginBackgroundTaskWithExpirationHandler: API for this purpose. Just be aware, if your task takes too long, it will be killed by the OS.
The NSURLConnection is indeed suspended and started again when the app enters the foreground. Just make sure you kill the connection if the app moves from suspended to not running like so:
- (void)applicationWillTerminate:(UIApplication *)application {
if (self.downloadConnection != nil){
[self.downloadConnection cancel];
}
}