• No results found

Multitasking with Handler and Message

CGet Application

3.2.3 Multitasking with Handler and Message

The Handler is the Swiss Army knife of messaging and scheduling operations for Android. This class allows you to queue tasks to be run on different threads and to schedule tasks using Message and Runnable objects.

The Android platform monitors the responsiveness of applications and kills those that are considered nonresponsive. An Application Not Responding (ANR) event occurs when no response is received to a user input for 5 seconds. When a user interacts with your application by touching the screen, pressing a key, or the like, your application must respond. So does this mean that every operation in your code must complete within 5 seconds? No, of course not, but the main UI thread does have to respond within that time frame. To keep the main UI thread snappy, any long-running tasks, such as retrieving data over the network, getting a large amount of data from a data- base, or performing complicated or time-consuming calculations, should be per- formed in a separate Thread, apart from the main UIThread.

Getting tasks into a separate thread and getting results back to the main UI thread is where the Handler and related classes come into play. When a Handler is created, it’s associated with a Looper. A Looper is a class that contains a MessageQueue and that processes Message or Runnable objects that are sent via the Handler.

When we used a Handler in listings 3.3 and 3.4, we created a Handler with a no- argument constructor. With this approach, the Handler is automatically associated with the Looper of the currently running thread, typically the main UI thread. The main UI thread, which is created by the process of the running application, is an instance of a HandlerThread. A HandlerThread is an Android Thread specialization that provides a Looper. The key parts involved in this arrangement are depicted in fig- ure 3.5.

When you’re implementing a Handler, you’ll have to provide a handle- Message(Message m) method. This method is the hook that lets you pass messages. When you create a new Thread, you can then call one of several sendMessage methods on Handler from within that thread’s run method, as our examples and figure 3.5

83

Working with views

this.reviews = reviews; }

@Override

public int getCount() { return this.reviews.size(); }

@Override

public Object getItem(int position) { return this.reviews.get(position); }

@Override

public long getItemId(int position) { return position;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) { Review review = this.reviews.get(position);

return new ReviewListView(

this.context, review.name, review.rating); }

private final class ReviewListView extends LinearLayout { private TextView name;

private TextView rating; public ReviewListView(

Context context, String name, String rating) { super(context); setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(5, 3, 5, 0);

this.name = new TextView(context); this.name.setText(name);

this.name.setTextSize(16f);

this.name.setTextColor(Color.WHITE); this.addView(this.name, params); this.rating = new TextView(context); this.rating.setText(rating); this.rating.setTextSize(16f); this.rating.setTextColor(Color.GRAY); this.addView(this.rating, params); } } }

The first thing to note in ReviewAdapter is that it extends BaseAdapter. BaseAdapter is an Adapter implementation that provides basic event-handling support. Adapter itself is an interface in the android.Widget package that provides a way to bind data to a View with some common methods. This is often used with collections of data, as we saw with Spinner and ArrayAdapter in listing 3.1. Another common use is with a Cur- sorAdapter, which returns results from a database (something you’ll see in chapter 5). Here we’re creating our own Adapter because we want it to return a custom View.

Override basic Adapter methods

B

Override Adapter getView

C

D

Define custom inner View class

Set Layout in code

E

Add TextView to tree

F

Our ReviewAdapter class accepts two parameters in the constructor and assigns those values to two simple member objects: Context and List<Review>. This class goes on to implement the straightforward required Adapter interface methods that return a count, an item, and an ID—we use the ordinal position in the collection as the ID

B

. The next Adapter method we have to implement is the most important— getView(). The Adapter returns any View we create for a particular item in the collec- tion of data that it’s supporting. Within this method, we get a particular Review object based on the position/ID, then we create an instance of a custom ReviewListView object to return as the View

C

.

ReviewListView itself, which extends LinearLayout (something you’ll learn more about in section 3.2.4), is an inner class inside ReviewAdapter; we never use it except to return a view from ReviewAdapter

D

. Within it, you see an example of setting layout and View details in code, rather than relying on their definition in XML. In this listing, we set the orientation, parameters, and margin for our layout

E

. Next, we populate the simple TextView objects that will be children of our new View and represent data. When these are set up via code, we add them to the parent container, which is in this case our custom class ReviewListView

F

. This is where the data binding happens— the bridge to the View from data. Another important thing to note about this is that we’ve created not only a custom View but also a composite one. We’re using simple existing View objects in a particular layout to construct a new type of reusable View, which shows the detail of a selected Review object on screen, as depicted in figure 3.2.

Our custom ReviewListView object is intentionally fairly simple. In many cases, you’ll be able to create custom views by combining existing views in this manner, though keep in mind that an alternative approach is to extend the View class itself. With this approach, you can implement core methods as desired and you have access to the lifecycle methods of a View. These View-specific methods include onMeasure(), onLayout(), onDraw(), onVisibilityChanged(), and others. Though we don’t need that level of control here, you should be aware that extending View gives you a great deal of power to create custom components.

Now that you’ve seen how you get the data for your reviews and what the Adapter and custom View we’re using look like, the next thing we need to do is take a closer look at a few more aspects of views, including layout.