|Android Adventures - Beginning Bitmap Graphics|
|Written by Mike James|
|Monday, 27 July 2015|
Page 4 of 5
Timer and Threads
Now we come to the inner workings of the animation. We need a Timer object that runs a function every so many milliseconds:
The timer object has a range of schedule functions which run a function, actually a TimerTask, at different times. The form we need is:
which runs the TimerTask after dealy milliseconds and every repeat milliseconds after that. The timings aren't accurate and it could take longer than specified for the TimerTask to be run.
The simplest way to create the TimerTask is to use an anonymous class:
This creates a new TimerTask and overrides its run method. The run method is called when the Timer is triggered. All it does is to call a new function update which we have yet to write that does the update to the ball's position etc. The final two parameters specify zero delay in triggering the first call and then 10 milliseconds as the repeat period. That is update will be called every 10 milliseconds if possible.
The update function is fairly easy:
First it sets the color to white and draws the ball, a circle, - this erases the ball at its old position. Next it updates the position by adding the velocities in each direction. To make sure that the ball bounces we test to see if it has reached a boundary and if it has its velocity is reversed. Finally, the color is set to red and the ball, a circle, is drawn at the new position.
If the function was to stop at this point then everything compiles and runs, but you won't see the ball move. The reason is simply that the UI is drawn once at the start of the program running and then only when it is necessary because the user has interacted with it or the orientation has changed, etc. As a result the bitmap displayed by the ImageView object would be changed every 10 milliseconds but it would not be redisplayed.
To make the UI update we need to call the ImageView's invalidate method which basically tells the UI to redraw it.
However if you put this in at the end of the update function you get an error message something like:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
The reason for this is that the Timer object uses a new thread to run the TimerTask. This is often what you want to happen but in this case it is a problem.
In case you are new to the idea of threading:
The big problem is that the UI event is really only happy when it has nothing to do. Then it just waits for an event and processes it at once. This makes the user think your app is very responsive because clicks and other input are acted on at once. If you give the UI thread a long task to do, for example you write a lot of processing into an event handler, then it isn't just waiting for the user to do something and the user starts to think that your app is slow and sluggish. At the extreme the UI thread can be kept 100% busy doing something and then the entire UI seems to freeze up.
The UI thread creates the UI and to avoid problems of synchronization only the UI thread can interact with the UI. This is a fairly common approach to implementing a UI and not at all unique to Android.
So what happens is that the Timer tries to run its TimerTask and this in turn runs the update function - but using the thread the Timer runs on not the UI thread.
Everything is fine until the last instruction of update - which attempts to use a method that belongs to an ImageView object and this it cannot do. Hence the error message.
At this point many Android programmers give up and try a completely different approach. Some of these approaches do have advantages - see the Handler class for example for a good alternative.
However the Android framework provides a method for just such a situation -
This is a method of the Activity object and you can use it from any thread that has access to the Activity object's methods to run a function on the UI thread. If the thread happens to be the UI thread then no harm done - the function is just called. If it isn't the UI thread then the call will be deferred until the UI thread is available and then the function will be run. As always the function shouldn't keep the UI thread busy for too long or the UI will become sluggish or even freeze completely.
The Runnable object has a single run method that is the function that is executed on the UI thread. So what we need to do in the Timer routine is:
This ensures that update is run on the UI thread.
Putting this all together gives:
which does look like a mess of nesting and brackets but you should be able to follow the logic. If you want to make things look simpler, then define the Runnable as a new class and perhaps even the TimerTask.
Now when you run the program you will see the red ball bounce slowly but smoothly around the screen. How good the animation is depends what you run it on. On the emulator is can be slow and irregular; on a real device it should be fine.
Note: animation complete with a trail to show how the ball moves
Now you know at least one way to allow non-UI thread interact with the UI.
There are so many ways to implement animation that this is just one of many starting points but with an understanding of this one the others will seem easier.
Even if you wanted to use this approach the structure of this demonstration program could be improved. For example the ball really should be a Ball class complete with its position and velocity properties and its move (update) method. This way you gain the benefits of object orientation and you can animate lots of balls around the screen with very little extra effort.
|Last Updated ( Wednesday, 12 October 2016 )|