Spinner animation during long operations - android-animation

My app has a not that frequent operation that is executed on the UI threads and takes long time (up to 3 secs). I want to display an animated 'wait' indications during that time. For example, a rotating spinner. No need to display the actual progress, just a fix speed animation.
I created a custom dialog that pops up during the long operation and it has this layout
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/spinner"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Problem is that it does not spin. How do I make it spin, even if the UI thread is busy?
I tried to create a chain on events to increment it but I get only two events, probably because the UI thread is busy.
// In the custom dialog class. mProgressBar is the ProgressBar in the layout.
// Called externally once when the dialog is shown
public void tick() {
mProgressBar.incrementProgressBy(10);
mProgressBar.postDelayed(new Runnable() {
#Override
public void run() {
// Not a recursion since it's done in a future event.
tick();
}
}, 100);
}
What is a simple way to achieve this animation? Will a frame by frame animation be easier to do?

Use AsyncTask http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html It is not spin because it is executing in UI thread

You may need to set true value to the android:indeterminate property in xml or code

Android: you're doing it wrong...
If you do something that takes a long time on the UI thread, your app freezes. Since you've locked the UI thread, you can't make anything animate (you're lucky to get two ticks), you can't make your app respond to touches or key presses, and your users will see an ANR screen (horrible user experience). Don't ever do any long running task on the UI thread, there is never any good reason to do so.
I'm guessing you want to do the task on the UI thread because what you display depends on the outcome of the task? In that case display a spinner on the main thread, run the task in the background (AsyncTask was designed for exactly this), and then only update you're UI after the task finishes. Same end result without the horrible user experience.

Related

How can I allow a user adjust an NSSlider without pausing the application update loop?

NOTE: Updated below...
I have a cocoa desktop application which consists of a series of controls around a custom NSView. I am using displayLink to drive the updates.
When a user clicks on an NSControl (a slider, a button, a checkbox, a radio button) the application appears to freeze until the mouse is released. I can confirm in fact that the displayLink callback (getFrameForTime) is NOT firing during the time. If I create a timer, that also does not fire, both remain paused until the user releases the mouse, at which point the application resumes updating.
The control is bound, and if I update that value from another thread (for example, via a callback from a MIDI interface) the slider behaves as expected: it moves, the value updates and the application does not pause.
I feel like this should be a fairly obvious fix, but I'm stumped.
Checking "continuous" in IB does as advertised: sends the values continuously, but still exhibits this behavior (preventing the UI update) until the mouse is released.
This seems to be related specifically to mouseDown on NSControl? Why would this block, and do I really need to subclass all my UI elements to change this behavior (seems extreme)
DisplayLink is in its own thread, so why mouseDown on the main thread block it? If this is the case, given the injunction on updating the Cocoa UI from other than the main thread, how do I deal with it?
Any help much appreciated.
Update
Per #Nikolai's comments below, I can confirm that using an NSTimer and adding it to NSEventTrackingRunLoopMode does NOT block. However, I would really like to use CVDisplayLink which (according to the documentation) runs in it's own thread and should not be blocked in this way. Unlike CADisplayLink, I cannot find a way to explicitly assign a runloop to CVDisplayLink (it seems it doesn't work that way), so perhaps the new question should be:
Why does CVDisplayLink block on NSEventTrackingRunLoopMode?
When clicking on an NSControl the runloop mode goes from NSDefaultRunLoopMode to NSEventTrackingRunLoopMode, as long as the mouse is down. That means that only run loop sources (display link) and timers fire that have been added to this mode.
You can add timers to any mode by using -[NSRunLoop addTimer:forMode:]. For a display link the equivalent method is -[CADisplayLink addToRunLoop:forMode:].
To make your animation continue during event tracking you would do something like:
[myDisplayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSEventTrackingRunLoopMode];
Your test project shows that you are calling a view's display method from within the display link's callback.
When commenting the display message out, the display link is called continuously even while moving the slider.
So what goes wrong is that when the runloop goes into event tracking mode, the call to display on the display link's thread blocks until the mouse is released and the run loop goes back to default mode. You can easily confirm this by putting a log statement before the call and one after it.
Why exactly that happens is not clear to me. What is clear is that it's illegal to call a view's methods from a background thread. You have to trigger the view's display by dispatching a setNeedsDisplay: on the main thread:
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
dispatch_async(dispatch_get_main_queue(), ^{
[(__bridge MyCustomView*)displayLinkContext setNeedsDisplay:YES];
});
return kCVReturnSuccess;
}

Display UI, from a background thread without breaking the flow

I have a code executing in the background thread, which is performing some kind of computation and is within a do-while loop. Due to some changes in the requirements, I have to display a UI to prompt user for input. This UI code will have to be done in the main thread, and after the prompt is entered, the logic needs to continue. Using a dispatch_async on main thread, I can display the UI, but Step -2 should not continue, until the UI is done. What is the best way to accomplish this, without breaking the flow of the code and moving units into blocks?
For example:
-(void) compute
{
do
{
//calculate some data
// Step -1...
...
// Step -2
...
...
} while(flag)
}
Between Step 1 and Step 2, I want to display a prompt. What is the best way to do so? Is it okay, to block this background thread using a mutex, which will get fired, by the main thread after the UI is done?
For this I would use GCD (Grand Central Dispatch). You can easily execute a block synchronously on the main thread (or asynchronously if you prefer), using dispatch_sync (or dispatch_async). I personally use my wrapper class EX2Dispatch in my EX2Kit library (https://github.com/einsteinx2/EX2Kit), but it's the same thing.
As an example, you would do something like this:
dispatch_sync(dispatch_get_main_queue(), ^{
// Do some stuff to the UI
};
EDIT:
I was reading it as needing to display information to the user based on the earlier calculation, but if you need a response from the user before continuing, then the loop needs to break after showing the alert, then be called again.
You could use an instance variable to track how far into the loop you are, so that it can be resumed at the same point in the UIAlertView's button clicked delegate method.
Try this
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(void) {
//do stuff here
});

iOS automatic threading?

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.

Best practice: what to do when view controller is loading very long?

I need an advice what to do when my view controller is loading quite long?
In my situation I have an offline map made with route-me and it takes several seconds to load the map from about 100mb database, then load a lot of markers, put them on the map, etc.
If I run this code in viewDidLoad UI seems unresponsive, because after pressing on tab or button nothing happens for a few seconds while everything is loading. If I put it in viewDidAppear map somehow doesn't get shown at all until I quit this view controller and go back to it.
If it takes so long you should show a spinner or other "busy" indicator while you load your data on a background thread.
Once your time-intensive process is complete, update the UI back on the main thread and hide your spinner/busy indicator.
Executing code on a background thread is extremely easy - there are several ways to do it - but the easiest/most straight-forward way is probably with performSelectorInBackground:withObject: as in this example:
[self performSelectorInBackground:#selector(loadMap) withObject:nil];
When you're ready to run on the main thread again - it's the same thing, but in reverse using performSelectorOnMainThread:withObject:waitUntilDone::
[self performSelectorOnMainThread:#selector(wrapupLoadMap) withObject:nil waitUntilDone:NO];
Good luck.

App freeze while Schedule LocalNotifications in background

- (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.