Previous section   Next section

Mobile Phone Handheld Hardware Hardware Rick Rogers John Lombardo O'Reilly Media, Inc. O'Reilly Media Android Application Development, 1st Edition

10.1. Android GUI Architecture

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

Figure 10-1. Model-View-Controller concept


10.1.1. The Model

The 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 View

The 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 Controller

The 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 Together

We 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:

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

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

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

  4. The application Model code starts playing the selected tune. In addition, it sends a redraw request to the UI framework.

  5. The redraw request is added to the event queue and eventually processed as described in Section 10.1.2.

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

          
      Previous section   Next section
     


    Treatment by Zetia