Smartphones and tablets include a browser so users can surf the Internet. However, a regular web page displayed on a small screen can be overwhelming. Content apps are apps that display selected content from a website so that the amount of information is manageable and the user experience is optimal. Such content apps exist for many sites, particularly social media websites and news sites. Often, websites offer frequently updated, consistently formatted information available to the outside world in general and app developers in particular. One such formatted information is called a Really Simple Syndication (RSS) feed. A typical RSS feed, not only is formatted in XML, but also often uses generic elements such as item
, title
, link
, description
, or pubDate
. In order to build a content app for a particular site, we need to understand the formatting of that website’s RSS feed, and we need to be able to parse the XML file that is exposed by that website so that we can display its contents.
An XML document is typically accompanied by a Document Type Declaration (DTD) document: it contains the rules that an XML document should adhere to. For example, it can specify that an rss
element can contain 0 or more item
elements. It could specify that an item
element must contain exactly one title
element and one link
element. It could also specify that a title
element must contain plain text. In this chapter, we focus on the XML only, and we do not take into account the DTD. We assume that any XML document that we parse is properly formed and complies with the rules of its DTD.
There are two types of parsers to parse an XML document:
▸ DOM parsers
▸ SAX parsers
A Document Object Model (DOM) parser converts an XML file into a tree. Once the XML has been converted into a tree, we can navigate the tree to modify elements, add elements, delete elements, or search for elements or values. This is similar to what a browser does with an HTML document: it builds a tree, and once that tree data structure is in memory, it can be accessed, searched, or modified using JavaScript.
Using a DOM parser is a good option if we use an XML document as a replacement for a database and we are interested in accessing and modifying that database. That is often not the case for content apps. Content apps typically convert the XML document into a list and allow the user to interact with the list. Often, that list contains links to URLs.
In order to convert an XML document into a list, we use a Simple API for XML (SAX) parser. A SAX parser builds a list as it reads the XML document. The elements of that list are objects. It is generally thought that a SAX parser is faster than a DOM parser.
For example, let’s assume that we want to parse the XML document shown in EXAMPLE 13.1.
A DOM parser would convert that XML document to a tree similar to the tree shown in FIGURE 13.1. There are actually a few more branches in the tree than Figure 13.1 shows because empty strings between elements are also branches of the tree.
Instead of looking at that XML document as a tree, we can also look at it as a list of three items. In this document, each item has a title and a link. We can code an Item
class with two String
instance variables, title
and link
. A SAX parser would convert that XML document to a list of items containing three elements as shown in FIGURE 13.2.
TABLE 13.1 The EntityResolver
, DTDHandler
, ContentHandler
, and ErrorHandler
interfaces
Interface | Receives Notifications For | Method Names |
---|---|---|
EntityResolver | Entity-related events | resolveEntity |
DTDHandler | DTD-related events | notationDcl, unparsedEntityDcl |
ContentHandler | Logic-related events | startDocument, endDocument, startElement, endElement, characters, and other methods |
ErrorHandler | Errors and warnings | error, fatalError, warning |
In Version 0 of our app, we concentrate on the process of XML parsing using a SAX parser. We use an existing SAX parser to parse the XML document in Example 13.1 and output feedback on the various steps of the parsing process to Logcat. In order to do this, we build two classes:
▸ The SAXHandler
class processes the various elements of the XML document, such as start tags, end tags, and text between tags, as they are encountered by the SAX parser.
▸ The MainActivity
class.
We will create a raw directory within the res directory and place a file named test.xml containing the XML document in Example 13.1 in it. Later in the chapter, we read an XML document dynamically from a URL. At this point, we do not create an Item
class yet. We do that in Version 1.
The DefaultHandler
class is the base class for SAX level 2 event handlers. The Default-Handler
class implements four interfaces: EntityResolver
, DTDHandler
, ContentHandler
, and ErrorHandler
. It implements all the methods that it inherits from these four interfaces as do-nothing methods. TABLE 13.1 shows these four interfaces and their methods.
When parsing an XML file using a SAX parser, there are many events that can happen: for example, whenever the parser encounters a start tag, an end tag, or text, it is an event. The DefaultHandler
class includes a number of methods that are automatically called when parsing a document and an event occurs. TABLE 13.2 shows some of these methods. For example, when the parser encounters a start tag, the startElement
method is called. When the parser encounters an end tag, the endElement
method is called. When the parser encounters text, the characters
method is called. These methods are implemented as do-nothing methods. In order to handle these events, we need to subclass the DefaultHandler
class and override the methods that we are interested in.
An XML document can contain entity references. An entity reference uses the special syntax &nameOfTheEntity;
. For example, "
is an entity reference that represents a double quote ("), <
represents the less than sign (<), and &
represents the ampersand character (&). These are some of several predefined entity references. We can also add user-defined entity references in the DTD. To keep things simple, we assume that the XML documents we parse in this chapter do not contain entity references.
TABLE 13.2 Selected methods of the DefaultHandler
class
Method | Description |
---|---|
void startElement( String uri, String localName, String startElement, Attributes attributes ) | Called when a start tag is encountered by the parser; uri is the namespace uri and localName its local name; startElement is the name of the start tag; attributes contains a list of (name, value) pairs that are found inside the startElement tag. |
void endElement( String uri, String localName, String endElement ) | Called when an end tag is encountered by the parser. |
void characters( char [ ] ch, int start, int length ) | Called when character data is encountered by the parser; ch stores the characters, start is the starting index, and length is the number of characters to read from ch. |
TABLE 13.3 Selected parse
methods of the SAXParser
class
Method | Description |
---|---|
void parse( InputStream is, DefaultHandler dh ) | Parses the content of the XML input stream is and uses dh to handle events. |
void parse( File f, DefaultHandler dh ) | Parses the content of the XML file f and uses dh to handle events. |
void parse( String uri, DefaultHandler dh ) | Parses the content of the XML file located at uri and uses dh to handle events. |
To parse an XML document, we call one of the parse
methods of the SAXParser
class. TABLE 13.3 lists some of them. They specify a DefaultHandler
parameter that is used to handle the events encountered during parsing. When we override the DefaultHandler
class, we need to override its methods so that we build our list of items inside these methods. EXAMPLE 13.2 shows our SAXHandler
class, Version 0. We override the startElement
, endElement
, and characters
methods, which all throw a SAXException
. Inside them, we output the values of some of their parameters. The SAX related classes are in the org.xml.sax
and org.xml.sax.helpers
packages. These classes are imported at lines 3–5.
As shown in Table 13.1, the DefaultHandler
class includes other methods, such as error
or fatalError
that are called when an error in the XML document is encountered. In order to keep things simple, we assume that there is no error of any kind in the XML and the DTD documents, so we do not deal with XML errors.
TABLE 13.4 Selected methods of the SAXParserFactory
class
Method | Description |
---|---|
SAXParserFactory newInstance( ) | Static method that creates and returns a SAXParserFactory. |
SAXParser newSAXParser( ) | Creates and returns a SAXParser. |
The SAXParser
class is abstract
so we cannot instantiate an object from it. The SAXParserFactory
class, also abstract
, provides methods to create a SAXParser
object. TABLE 13.4 lists two of its methods. The static
newInstance
method creates a SAXParserFactory
object and returns a reference to it, and the newSAXParser
method creates a SAXParser
object and returns a reference to it.
TABLE 13.5 The openRawResource
method of the Resources
class
Method | Description |
---|---|
InputStream openRawResource( int id ) | Opens and returns an InputStream to read the data of the resource whose id is id. |
To create a SAXParser
object, we can use the following sequence:
SAXParserFactory factory = SAXParserFactory.newInstance( ); SAXParser saxParser = factory.newSAXParser( );
In Version 0, we store a hard coded XML document in a file located in the raw directory of the res directory. Thus, if the name of our file is test.xml, we can access it using the resource R.raw.test
. We can convert that resource to an input stream and use the first parse
method listed in Table 13.3.
The getResources
method of the Context
class, inherited by the Activity
class, returns a Resources
reference to our application package. With that Resources
reference, we can call the openRawResource
method, listed in TABLE 13.5, in order to open and get a reference to an InputStream
so that we can read the data in that resource.
We can chain method calls to getResources
and openRawResource
to open an input stream to read data from the file test.xml as follows:
InputStream is = getResources( ).openRawResource( R.raw.test );
In order to parse an InputStream
named is
containing an XML document with a SAXParser
named saxParser
that uses a DefaultHandler
reference named handler
to handle the events, we call the parse
method as follows:
saxParser.parse( is, handler );
EXAMPLE 13.3 shows the MainActivity
class. The SAXParser
and SAXParserFactory
classes, both part of the javax.xml.parsers
package, are imported at lines 7–8. Many exceptions can be thrown by the various methods that we use:
newSAXParser
throws aParserConfigurationException
and aSAXException
.
openRawResource
throws aResources.NotFoundException
.
parse
throws anIllegalArgumentException
, anIOException
, and aSAXException
.
Some of these exceptions are checked and some others are unchecked. To keep things simple, we use one try
block at lines 14–20 and catch
a generic Exception
at line 20. Inside the try
block, we create a SAXParser
at lines 15–16 and declare and instantiate a SAXHandler
named handler
at line 17. We open an input stream to read the data in the test.xml file located in the raw directory of the res directory at line 18, and start parsing that input stream with handler
at line 19. The parsing triggers a number of calls to the various methods inherited from DefaultHandler
by SAXHandler
, in particular the three methods that we overrode in Example 13.2.
FIGURE 13.3 shows the output inside Logcat when we run the app. We observe the following:
▸ Whenever a start tag is encountered, we execute inside the startElement
method, and the value of the startElement
parameter is the name of the tag.
▸ Whenever an end tag is encountered, we execute inside the endElement
method, and the value of the endElement
parameter is the name of the tag.
▸ Between the start and end tags of the same element, we execute inside the characters
method and the array ch
stores the characters between the start and end tags (also called the element contents).
▸ Between the end tag of an element and the start tag of another element, we execute inside the characters
method and the text only contains white space characters.
In Version 1, we parse the test.xml file and convert it to a list. We code the Item
class as part of our Model. The Item
class encapsulates an item in the XML document: it has two String
instance variables, title
and link
. In the SAXHandler
class, we build an ArrayList
of Item
objects that reflect what we find in the XML document.
EXAMPLE 13.4 shows the Item
class. It includes an overloaded constructor at lines 7–10, mutators at lines 12–13, and accessors at lines 14–15. We add a toString
method at line 16, mostly to check the correctness of the parsing.
In the SAXHandler
class, Version 1, shown in EXAMPLE 13.5, we build an ArrayList
of Item
objects. When the startElement
method is called, if the tag is item
, we need to instantiate a new Item
object. If the tag is title
or link
, the text that we will read inside the characters
method is the value for the title
or link
instance variable of the current Item
object. When the endElement
method is called, if the tag is item
, we are done processing the current Item
object and we need to add it to the list.
We need to be careful that inside the characters
method, we process the correct data. There are white spaces that are typically present between tags, for example between an end tag and a start tag, in an XML document. We must be careful not to process them. The character data that we want to process is found between a start tag and the corresponding end tag. Thus, we use a state variable, validText
, to assess if the character data that we process inside characters is the data that we want to process or not. When we encounter a start tag, we set validText
to true
. When we encounter an end tag, we set validText
to false
. We only process the character data in the characters
method if validText
is true
.
We declare four instance variables at lines 9–12:
▸ validText
, a boolean
state variable. If its value is true
, we process character data inside the characters
method, otherwise we do not.
▸ element
, a String
, stores the current element (xml
, rss
, item
, title
, or link
in this example).
▸ currentItem
, an Item
, stores the current Item
object.
▸ items
, an ArrayList
, stores the list of Item
objects that we build.
The constructor, at lines 14–17, initializes validText
to false
at line 15 and instantiates items
at line 16. We code an accessor to items
at line 19 so we can access it from an outside class.
In the startElement
method (lines 21–28), we set validText
to true
at line 24 and assign startElement
, the text inside the start tag, to element
at line 25. If the start tag is equal to item
(line 26), we instantiate a new Item
object and assign its reference to currentItem
at line 27.
If there is an end tag following the start tag, the characters
method is called next. If currentItem
is not null
and validText
is true
and the value of element
is either title
or link
(lines 39 and 41), we are in the middle of building the current Item
object. If element
is equal to title
or link
, we set the value of the title
or link
instance variable of the current Item
object to the characters read (lines 40 and 42).
After the startElement
and characters
methods are called, the endElement
method (lines 30–35) is called. We assign false
to validText
at line 32. In this way, any text found after an end tag and before a start tag is not processed by the characters
method. If the value of element is item
(line 33), we are done processing the current Item
object, and we add it to items
at line 34.
EXAMPLE 13.6 shows the MainActivity
class, Version 1. At line 22, we retrieve the list of items that we parsed and assign it to the ArrayList
items
. At lines 23–24, we loop through it and output it to Logcat in order to check the correctness of the parsing.
FIGURE 13.4 shows the output inside Logcat when we run the app, Version 1. We can check that the ArrayList
items
contains the three Item
objects that are listed in the test.xml file.
In Version 2, we parse an XML document from a remote website. Instead of reading data from the test.xml file, we pull data from the rss feed for the computer science blog of Jones & Bartlett Learning. The URL is http://www.blogs.jblearning.com/computer-science/feed/. We access the Internet, open that URL, and read the data at that URL. Starting with Version 3.0, Android does not allow an app to open a URL in its main thread. A thread is a sequence of code that executes inside an existing process. The existing process is often called the main thread. For an Android app, it is also called the user-interface thread. There can be several threads executing inside the same process. Threads can share resources, such as memory.
When we start the app, our code executes in the main thread, so we need to open that URL in a different thread. Furthermore, we need the XML parsing in the secondary thread to finish before trying to fill the list with data in the main thread because we use the data read in the secondary thread for the list. The AsyncTask
class, from the android.os
package, allows us to perform a background operation and then access the main thread to communicate its results. It is designed to execute short tasks, lasting a few seconds or less, and should not be used to start a thread that runs for a long time. AsyncTask
is abstract
so we must subclass it and instantiate an object of the subclass in order to use it. Appendix D explains the AsyncTask
in detail, and it also includes a very simple app using AsyncTask
.
We create the ParseTask
class, a subclass of the AsyncTask
class, in order to parse an rss feed from a remote server. We perform the following steps:
▸ We create ParseTask
, a subclass of AsyncTask.
▸ We override the doInBackground
method so that it parses the XML document we are interested in.
▸ Inside ParseTask
, we include an instance variable that is a reference to the activity that starts the task. This enables us to call methods of the activity class from ParseTask
.
▸ We override the onPostExecute
method and call methods of the activity class to update the activity with the results of the task that we just completed.
▸ Inside the activity, we create and instantiate an object of ParseTask
.
▸ We pass a reference to this activity to the ParseTask
object.
▸ We call the execute
method of AsyncTask
, passing a URL, to start executing the task.
The AsyncTask
class uses three generic types that we must specify when extending it. The class header of a subclass is as follows:
AccessModifier ClassName extends AsyncTask<Params, Progress, Result>
where Params
, Progress
, and Result
are placeholders for actual class names and have the following meanings:
▸ Params
is the data type of the array that is passed to the execute
method of AsyncTask
when we call it.
▸ Progress
is the data type used for progress units as the task executes in the background. If we choose to report progress, that data type is often Integer
or Long
.
▸ Result
is the data type of the value returned upon execution of the task.
In this app, we want to be able to pass an array of Strings
(representing URLs) as the argument of the execute
method, we do not care about reporting progress, and the task returns an ArrayList
of Item
objects. Thus, we use the following class header:
public ParseTask extends AsyncTask<String, Void, ArrayList<Item>>
TABLE 13.6 Selected methods of the AsyncTask
class
Method | Description |
---|---|
AsyncTask execute( Params. . . params ) | params represent an array of values of type Params, a generic type. |
Result doInBackground( Params. . . params ) | params is the argument passed to execute when we call it. |
void onPostExecute( Result result ) | Automatically called after doInBackground finishes. Result is the value returned by doInBackground. |
In order to launch a task using the AsyncTask
class, we do the following:
▸ Create a subclass of AsyncTask
.
▸ Declare and instantiate an object of that subclass.
▸ With that object, call the execute
method to start the task.
After we call the execute
method with a ParseTask
reference, the doInBackground
method executes and its argument is the same as the one we passed to execute
. Its return value becomes the argument of onPostExecute
, which is called automatically after doInBackground
finishes. TABLE 13.6 shows these three methods. The execute
and doInBackground
method headers show that they both accept a variable number of arguments.
When we call the execute
method, the following methods of the AsyncTask
class execute in this order: onPreExecute
, doInBackground
, and onPostExecute
.
Before the task executes, if we want to perform some initialization, we can place that code inside the onPreExecute
method. We place the code for the task we want to execute in the doInBackground
method. That method is abstract
and must be overridden. The argument that is automatically passed to the doInBackground
method is the same argument that we passed to the execute
method when we called it. Inside doInBackground
, as the task is executing, if we want to report progress to the main thread, we can call the publishProgress
method. That in turn triggers a call to the onProgressUpdate
method, which we can override: for example, we can update a progress bar in the user-interface thread. When the doInBackground
method finishes executing, the onPostExecute
method is called. We can override it to update the user interface with the results of the task that just completed. The argument that is automatically passed to the onPostExecute
method is the value that is returned by the doInBackground
method.
To summarize the flow of data, the argument that we pass to execute
is passed to doInBackground
and the returned value of doInBackground
is passed as the argument of onPostExecute
.
In our ParseTask
class, the Params data type is String
and the Result data type is ArrayList
. Thus, the doInBackground
method has the following method header:
protected ArrayList<Item> doInBackground( String... urls )
EXAMPLE 13.7 shows the ParseTask
class. In the class header (line 9), we specify String
as the Params
data type, Void
as the Progress
data type since we do not report progress in this app, and ArrayList
as the Result
data type.
At line 10, we declare the activity
instance variable, a reference to MainActivity
. The constructor (lines 12–4), accepts a MainActivity
parameter and assigns it to activity
. When we call that constructor from the MainActivity
class, we pass this
as the argument.
We override the doInBackground
method at lines 16–27. The method header specifies that it returns an ArrayList
of Item
objects (the Result
data type) and that it accepts Strings
as parameters (the Params
data type).
The code to parse the XML is essentially the same as in Example 13.6 except that we use a parse
method whose first parameter is a String
representing a URL at line 21, as opposed to an InputStream
before. We expect that only one argument is passed to that method so we only process the first argument, which we access using the expression urls[0]
. We do not expect that urls
will be null
at that point but even if it is, we catch that exception at line 23. At line 22, we return the ArrayList
of Item
objects generated when we parse the XML document.
We override the onPostExecute
method at lines 29–31. When it is called automatically after the doInBackground
method finishes execution, the value of its parameter returnedItems
is the ArrayList
of Item
objects returned by doInBackground
. We call the displayList
method of the MainActivity
class with the activity
instance variable, passing returnedItems
at line 30. Inside the MainActivity
class, we need to code the displayList
method.
Since our app accesses the Internet, we need to add the appropriate permission code inside the manifest
element of the AndroidManifest.xml file as follows:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
EXAMPLE 13.8 shows the MainActivity
class. We declare a String
constant named URL
at lines 9–10 to store the URL of the RSS feed. At line 15, we declare and instantiate a ParseTask
object and call the execute
method at line 16, passing URL
. Since the execute
method accepts a variable number of arguments, we can pass 0, 1, or more arguments. Alternatively, we could have passed an array of Strings
containing only one element, URL
, as follows:
task.execute(new String[ ] { URL } );
At lines 19–24, we code the displayList
method. At this point, it outputs the contents of its parameter items
to Logcat. Items
could be null
so we test for it at line 20 before the for
loop.
Note that the sequencing of our code is important when we use an AsyncTask
. After the call to execute
at line 16, execution would continue inside the onCreate
method if there were statements after line 16. In that case, there would be interleaved execution between those statements and the statements executing inside the ParseTask
class. If we attempted to retrieve the ArrayList
of Item
objects right after line 16 inside the onCreate
method, it would very likely be null
. The correct way to process the result of a task is to do it inside the onPostExecute
method.
FIGURE 13.5 shows a partial output inside Logcat when we run the app, Version 2. Note that this is a live blog, therefore contents may change daily and may not match the output in the figure.
ListView
, Web Content App, Version 3In Versions 0, 1, and 2, we do not have any user interface. In Version 3, we display a list of all the titles retrieved from the XML document on the screen.
In order to keep this example simple, we only allow our app to run in vertical position. Thus, we add the following to the AndroidManifest.xml file inside the activity
element:
android:screenOrientation="portrait"
To display the titles as a list on the screen, we use a ListView
element in the activity_main.xml file. A ListView
contains an arbitrary number of Strings
. We do not have to specify how many elements when we define the ListView
. Thus, we can build an ArrayList
of Item
objects dynamically and place the corresponding list of titles inside the ListView
programmatically.
EXAMPLE 13.9 shows the activity_main.xml file, Version 3. We replace the TextView
that displays Hello World!
in the skeleton file with a ListView
element at lines 12–16. We give it an id at line 12 so that we can access it by code using the findViewById
method and populate it with data. We use the android:divider
and the android:dividerHeight
attributes at lines 15–16 to add a red, 2
pixels thick dividing line between the items displayed.
EXAMPLE 13.10 shows the ParseTask
class, Version 3. At lines 24–25, we replace the output statement to Logcat at line 24 of Example 13.7 with a Toast
statement. Along with the statement importing Toast
at line 4, these are the only changes.
EXAMPLE 13.11 shows the MainActivity
class, Version 3. We include a ListView
instance variable, listView
, at line 13.
Inside the displayList
method (lines 23–36), we populate listView
with all the titles from the ArrayList items
. We generate titles
, an ArrayList
of Strings
containing the titles in items
at lines 25–28. At lines 30–31, we create an ArrayAdapter
containing titles
. We use the constructor listed in TABLE 13.7, passing three arguments. The first argument is this
, a reference to this activity (Activity
inherits from Context
). The second argument is android.R.layout.simple_list_item_1
, a constant of the R.layout
class. It is a simple layout that we can use to display some text. The third argument is titles
, an ArrayList
and therefore a List
.
TABLE 13-7 Selected constructor of the ArrayAdapter
class
Constructor | Description |
---|---|
ArrayAdapter( Context context, int textViewResourceId, List<T> objects ) | Context is the current context; textViewResourceId is the id of a layout resource containing a TextView; objects is a list of objects of type T. |
We retrieve the ListView
defined in activity_main.xml using its id at line 18 and assign it to listView
. At line 32, we assign the list of titles as the list to display inside listView
, calling the setAdapter
method of the ListView
class, shown in TABLE 13.8. The adapter
reference is actually an ArrayAdapter
and therefore a ListAdapter
because ArrayAdapter
inherits from the ListAdapter
interface. ListAdapter
is a bridge between a ListView
and its list of data.
TABLE 13.8 The setAdapter
method of the ListView
class
Method | Description |
---|---|
void setAdapter( ListAdapter adapter ) | Sets the ListAdapter of this ListView to adapter; adapter stores the list of data backing this ListView and produces a View for each item in the list of data. |
At this point, we do not set up any list event handling; we do that in Version 4. If items
is null
, we show a Toast
at lines 34–35.
FIGURE 13.6 shows the app, Version 3, running inside the emulator.
In Version 4, we open the browser at the URL chosen by the user from the list. In order to do that, we do the following:
▸ Implement event handling when the user selects an item from the list.
▸ Retrieve the link associated with that item.
▸ Open the browser at the URL contained in the link.
In Version 3, our list shows the titles of the ArrayList
of Item
objects that we generated parsing the XML document. In Version 4, we actually need to access the link values. In order to retrieve the link for a given Item
object, we add an instance variable to the MainActivity
class that represents the list of Item
objects. When the user selects an item from the list, we retrieve its index, retrieve the Item
object from the ArrayList
at that index, and retrieve the value of the link
instance variable of that Item
.
EXAMPLE 13.12 shows the MainActivity
class, Version 4. At line 18, we declare the listItems
instance variable, an ArrayList
of Item
objects. Inside displayList
, at line 29, we assign its items
parameter to listItems
.
OnItemClickListener
is a public
inner interface of the AdapterView
class. It includes one abstract
callback method, onItemClick
. That method is automatically called when the user selects an item from a list associated with an AdapterView
, provided that an OnItemClickListener
object is listening for events on that list. TABLE 13.9 shows the onItemClick
method.
In order to implement list event handling, we do the following:
▸ We code a private
handler class that extends OnItemClickListener
at lines 47–56, and override its only abstract
method, onItemClick
, at lines 49–55.
▸ We declare and instantiate an object of that class at line 40.
▸ We register that handler on the ListView
at line 41 so that it listens to list events.
TABLE 13.9 The OnItemClickListener
interface
Method | Description |
---|---|
void onItemClick( AdapterView<?> parent, View view, int position, int id ) | This method is called when an item is selected in a list: parent is the AdapterView; view is the view within the AdapterView that was selected; position is the position of view within the AdapterView; id is the row index of the item selected. |
TABLE 13.10 The parse
method of the Uri
class
Method | Description |
---|---|
static Uri parse( String uriString ) | Creates a Uri that parses uriString and returns a reference to it. |
The parameter position
of the onItemClick
method stores the index of the item in the list that the user selected. We use it to retrieve the Item
object in listItems
at that index at line 51. At line 52, we use the static parse
method of the Uri
class, shown in TABLE 13.10, to create a Uri
object with the String
stored in the link
instance variable of that Item
object. At line 53, we create an Intent
to open the web browser at that Uri
. We pass the ACTION_VIEW
constant of the Intent
class and the Uri
object to the Intent
constructor. At line 54, we start a new activity with that Intent
. Since the activity is to open the browser, there is no layout file and class associated with that activity.
FIGURE 13.7 shows the app, Version 4, running inside the emulator, after the user selects an item from the list. If we click on the back button of the emulator, we go back to the list of links, which then becomes the activity at the top of the activity stack.
There are two types of XML parsers: DOM and SAX.
A DOM parser converts an XML document into a tree. A SAX parser reads the XML document sequentially and reports what it finds. We can use it to convert the XML document to a list of objects.
We can use the SAXParserFactory
class to get a SAXParser
reference.
The SAXParser
class contains several methods to parse an XML document.
We can extend the DefaultHandler
class to parse an XML document using a SAX parser.
The DefaultHandler
class includes callback methods that are automatically called when parsing an XML document. For example, the startElement
method is called when the parser encounters a start
tag.
The latest versions of Android do not allow us to open a URL on the app’s main thread. We need to do that on a separate thread.
We can extend the AsyncTask
class to provide threading functionality.
A call to the execute
method of the AsyncTask
class triggers execution of its preExecute
, doInBackground
, and postExecute
methods, in that order.
The argument passed to execute
is in turn passed to doInBackground
, and the return value of doInBackground
is then passed as the argument of postExecute
.
In order to access the Internet from an app, we need to include a uses-permission
element for it in the AndroidManifest.xml file.
We can use a ListView
to display a list of items.
To perform event handling on a ListView
, we can implement the OnItemClickListener
inner interface of the AdapterView
class and override its onItemClick
method.
The Uri
class encapsulates a Uniform Resource Identifier.
We can create an intent to open the browser using the ACTION_VIEW
constant of the Intent
class and an Uri
object.
What does DOM stand for?
Direct Object Model
Document Object Model
Document Oriented Model
Direct Oriented Model
What does SAX stand for?
Super Adapter XML
Simple API eXtension
Simple API for XML
Simple Android for XML
What method of the DefaultHandler class is called when the parser encounters a start tag?
startElement
startTag
start
tag
What method of the DefaultHandler class is called when the parser encounters an end tag?
endElement
endTag
end
tag
What method of the DefaultHandler class is called when the parser encounters character data?
data
characterData
foundCharacters
characters
What method of the AsyncTask class do we call to start the task?
start
run
execute
startTask
The argument of the method in question 7 becomes the argument of which method?
preExecute
doInBackground
postExecute
publishProgress
The return value of the doInBackground method becomes the argument of which method?
preExecute
doInBackground
postExecute
publishProgress
What constant of the Intent class can we use to create an intent to open the browser?
ACTION_BROWSER
ACTION_VIEW
WEB_VIEW
WEB_BROWSER
This code starts parsing a file named myFile.xml located in the raw directory of the res directory, using the DefaultHandler myHandler (assume that SAXHandler extends DefaultHandler).
SAXParserFactory factory = SAXParserFactory.newInstance( ); SAXParser saxParser = factory.newSAXParser( ); SAXHandler myHandler = new SAXHandler( ); // Your code goes here
Inside the characters method of the DefaultHandler class, assign to a String named myString the characters read by the parser.
public void characters( char ch [ ], int start, int length ) throws SAXException { String myString; // Your code goes here }
We are coding a class named MyTask, extending AsyncTask. We expect to pass an array of Integers to the execute method, and we expect the doInBackground method to return an ArrayList of Doubles. We do not care about reporting progress. Write the class header of MyTask.
We are coding a class named MyTask2, extending AsyncTask. We expect to pass a Double to the execute method, and we expect the doInBackground method to return a String. We want to report progress as an Integer. Write the class header of MyTask2.
We have already coded a class named MyTask, extending AsyncTask. The doInBackground accepts a variable number of Integers as parameters. Start executing the task, passing one or more values.
// We are inside an activity class MyTask myTask = new MyTask( this ); // Your code goes here ( one statement only )?
Write a private class that implements the OnItemClickListener interface and override its method(s) as do-nothing method(s).
Inside an activity, write the code to open the browser at http://www.google.com.
Write an app similar to the app in the chapter: use a different URL and display the published date along with the title.
Write an app similar to the app in the chapter: use a different URL and display only the titles that have been published within the last 30 days.
Write an app similar to the app in the chapter: use a different URL and add a text field to let users specify how recent they want the data to be. For example, if a user enters 45, then only display results with a published date that is within the last 45 days.
Write an app similar to the app in the chapter: use a different URL and add a front end activity with a text field to let the user specify a search term so that the titles displayed include that search term.
Write an app that presents the user with a list of websites of your choice in a ListView. When the user clicks on one, the browser opens that website.
44.200.94.150