Mobile Phone Handheld Hardware Hardware Rick Rogers John Lombardo O'Reilly Media, Inc. O'Reilly Media Android Application Development, 1st Edition10.1. Android GUI ArchitectureThe Android environment adds yet another Graphical User Interface (GUI)
toolkit to the Java ecosphere, joining AWT, Swing, SWT, and J2ME (leaving
aside the web UI toolkits). If you've worked with any of these, the
Android framework will look familiar. Like them, it is single-threaded,
event-driven, and built on a library of nestable components. The Android UI framework is, like the other UI frameworks, organized
around the common Model-View-Controller pattern illustrated in Figure 10-1. It provides structure and tools
for building a Controller that handles user input (like key presses and
screen taps) and a View that renders graphical information to the
screen. 
10.1.1. The ModelThe Model is the guts of your application: what it actually does.
It might be, for instance, the database of tunes on your device and the
code for playing them. It might be your list of contacts and the code
that places phone calls or sends IMs to them. While a particular application's View and Controller will
necessarily reflect the Model they manipulate, a single Model might be used
by several different applications. Think, for instance, of an MP3 player
and an application that converts MP3 files into WAV files. For both
applications, the Model includes the MP3 file format and codecs for it.
The former application, however, has the familiar Stop, Start, and Pause
controls, and plays the track. The latter may not produce any sound at
all; instead, it will have controls for setting bitrate, etc. The Model
is all about the data. It is the subject of most of the rest of this
book. 10.1.2. The ViewThe View is the application's feedback to the user. It is the
portion of the application responsible for rendering the display,
sending audio to speakers, generating tactile feedback, and so on. The
graphical portion of the Android UI framework's View, described in
detail in Chapter 12, is implemented as a
tree of subclasses of the View class.
Graphically, each of these objects represents a rectangular area on
the screen that is completely within the rectangular area represented by
its parent in the tree. The root of this tree is the application
window. As an example, the display in a hypothetical MP3 player might
contain a component that shows the album cover for the currently playing
tune. Another component might display the name of the currently playing
song, while a third contains subcomponents such as the Play, Pause, and
Stop buttons. The UI framework paints the screen by walking the View tree,
asking each component to draw itself in a preorder traversal. In other
words, each component draws itself and then asks each of its children to
do the same. When the whole tree has been rendered, the smaller, nested
components that are the leaves of the tree—and that were, therefore,
painted later—appear to be painted on top of the components that are
nearer to the root and that were painted first. The Android UI framework is actually quite a bit more efficient
than this oversimplified description suggests. It does not paint an area
of a parent view if it can be certain that some child will later paint
the same area, because it would be a waste of time to paint background
underneath an opaque object! It would also be a waste of time to repaint
portions of a view that have not changed. 10.1.3. The ControllerThe Controller is the portion of an application that responds to
external actions: a keystroke, a screen tap, an incoming call, etc. It
is implemented as an event queue. Each external action is represented as
a unique event in the queue. The framework removes each event from the
queue in order and dispatches it. For example, when a user presses a key on his phone, the Android
system generates a KeyEvent and adds
it to an event queue. Eventually, after
previously enqueued events have been processed, the KeyEvent is removed from the queue and passed
as the parameter of a call to the dispatchKeyEvent method of the View that is
currently selected. Once an event is dispatched to the in-focus component, that
component may take appropriate action to change the internal state of
the program. In an MP3 player application, for instance, when the user
taps a Play/Pause button on the screen and the event is dispatched to
that button's object, the handler method might update the Model to
resume playing some previously selected tune. This chapter describes the construction of a Controller for an
Android application. 10.1.4. Putting It TogetherWe now have all the concepts necessary to describe the complete UI
system. When an external action occurs (for example, when the user
scrolls, drags, or presses a button; a call comes in; or an MP3 player
arrives at the end of its playlist), the Android system enqueues an
event representing the action on the event queue. Eventually the
event is dequeued—first in, first out—and dispatched to an
appropriate event handler. The handler, probably code you write as part
of your application, responds to the event by notifying the Model that
there has been a change in state. The Model takes the appropriate
action. Nearly any change in Model state will require a corresponding change in the View. In
response to a key press, for instance, an EditText component must show the newly typed
character at the insertion point. Similarly, in a phone book
application, clicking on a contact will cause that contact to be
highlighted and the previously highlighted contact to have its
highlighting removed. In order to update the display, the Model must notify the UI
Framework that some portion of the display is now stale and has to be
redrawn. The redraw request is, actually, nothing more than another
event enqueued in the same framework event queue that held the
Controller event a moment ago. The redraw event is processed, in order,
like any other UI event. Eventually, the redraw event is removed from the queue and
dispatched. The event handler for a redraw event is the View. The
View tree is redrawn, and each
View object is responsible for rendering its current
state at the time it is drawn. To make this concrete, we can trace the cycle through a
hypothetical MP3 player application: When the user taps the screen image of the Play/Pause button,
the framework creates a new MotionEvent containing, among other
things, the screen coordinates of the tap. The framework enqueues
the new event at the end of the event queue. As described in Section 10.1.3, when the event percolates
through the queue, the framework removes it and passes it down the
View tree to the leaf widget within whose bounding rectangle the tap
occurred. Because the button widget represents the Play/Pause button,
the application button handling code tells the core (the Model) that
it should resume playing a tune. The application Model code starts playing the selected tune.
In addition, it sends a redraw request to the UI framework. The redraw request is added to the event queue and eventually
processed as described in Section 10.1.2. The screen gets redrawn with the Play button in its playing
state, and everything is again in sync.
UI component objects such as buttons and text boxes actually implement
both View and Controller methods. This only makes sense. When you add a
Button to your application's UI, you
want it to appear on the screen as well as do something when the user
pushes it. Even though the two logical elements of the UI—the View and
the Controller—are implemented in the same object, you should take care
that they do not directly interact. Controller methods, for instance,
should never directly change the display. Leave it to the code that
actually changes state to request a redraw, and trust that later calls
to rendering methods will allow the component to reflect its new state.
Coding in this way minimizes synchronization problems and helps to keep
your program robust and bug-free. There is one more aspect of the Android UI framework that it is
important to understand: it is single-threaded. There is a single thread
removing events from the event queue to make Controller callbacks and to
render the View. This is significant for several reasons. The simplest consequence of a single-threaded UI is that it is not
necessary to use synchronized
blocks to coordinate state between the View and the Controller.
This is a valuable optimization. Another advantage of a single-threaded UI is the guarantee that
each event on the event queue is processed completely and in the order
in which it was enqueued. That may seem fairly obvious, but its
implications make coding the UI much easier. When a UI component is
called to handle an event, it is guaranteed that no additional UI
processing will take place until it returns. That means, for instance,
that when a component requests multiple changes in the program
state—each of which causes a corresponding request that the screen be
repainted—it is guaranteed that the repaint will
not start until it has completed processing,
performed all of its updates, and returned. In short, UI callbacks are
atomic. There is a third reason to remember that there is only a single
thread dequeuing and dispatching events from the UI event queue: if your
code stalls that thread, for any reason, your UI will freeze! If a
component's response to an event is simple (changing the state of
variables, creating new objects, etc.), it is perfectly correct to do
that processing on the main event thread. If, on the other hand, the
handler must retrieve a response from some distant network service or
run a complex database query, the entire UI will become unresponsive
until the request completes. That definitely does not make for a great
user experience! Long-running tasks must be delegated to another thread,
as described in Section 10.3.5.
 |