Not surprisingly, the Android platform gives developers a wide range of ways to make use of this Internet access.. The emphasis of this book is on the higher-level forms of access: the W
Trang 1253
Chapter
Communicating via the
Internet
The expectation is that most, if not all, Android devices will have built-in Internet access
That could be Wi-Fi, cellular data services (EDGE, 3G, etc.), or possibly something else
entirely Regardless, most people—or at least those with a data plan or Wi-Fi access—
will be able to get to the Internet from their Android phone
Not surprisingly, the Android platform gives developers a wide range of ways to make
use of this Internet access Some offer high-level access, such as the integrated WebKit
browser component If you want, you can drop all the way down to using raw sockets In
between, you can leverage APIs—both on-device and from third-party JARs—that give
you access to specific protocols: HTTP, XMPP, SMTP, and so on
The emphasis of this book is on the higher-level forms of access: the WebKit
component (discussed in Chapter 13) and Internet-access APIs (discussed in this
chapter) As busy coders, we should be trying to reuse existing components versus
rolling our own on-the-wire protocol wherever possible
REST and Relaxation
Android does not have built-in SOAP or XML-RPC client APIs However, it does have the
Apache HttpComponents library baked in You can either layer a SOAP/XML-RPC layer
atop this library or use it “straight” for accessing REST-style web services For the
purposes of this book, REST-style web services are considered simple HTTP requests
for ordinary URLs over the full range of HTTP verbs, with formatted payloads (XML,
JSON, etc.) as responses
More expansive tutorials, FAQs, and HOWTOs can be found at the HttpClient web site
(http://hc.apache.org/) Here, we’ll cover the basics, while checking the weather
25
Trang 2HTTP Operations via Apache HttpClient
The HttpClient component of HttpComponents handles all HTTP requests on your behalf The first step to using HttpClient is, not surprisingly, to create an HttpClient object Since HttpClient is an interface, you will need to actually instantiate some implementation of that interface, such as DefaultHttpClient
Those requests are bundled up into HttpRequest instances, with different HttpRequest implementations for each different HTTP verb (e.g., HttpGet for HTTP GET requests) You create an HttpRequest implementation instance, fill in the URL to retrieve and other configuration data (e.g., form values if you are doing an HTTP POST via HttpPost), and then pass the method to the client to actually make the HTTP request via execute() What happens at this point can be as simple or as complicated as you want You can get an HttpResponse object back, with a response code (e.g., 200 for OK), HTTP
headers, and the like Or, you can use a flavor of execute() that takes a
ResponseHandler<String> as a parameter, with the net result being that execute() returns just the String representation of the response body In practice, this is not a recommended approach, because you really should be checking your HTTP response codes for errors However, for trivial applications, like book examples, the
ResponseHandler<String> approach works just fine
For example, let’s take a look at the Internet/Weather sample project This implements
an activity that retrieves weather data for your current location from the National
Weather Service (Note that this probably only works only for geographic locations in the United States.) That data is converted into an HTML page, which is poured into a WebKit widget for display Rebuilding this demo using a ListView is left as an exercise for the reader Also, since this sample is relatively long, we will only show relevant pieces of the Java code here in this chapter, though you can always download the full source code from the Apress web site
To make this a bit more interesting, we use the Android location services to figure out where we are—well, sort of The full details of how that works are left until Chapter 32
In the onResume() method, we toggle on location updates, so we will be informed where
we are now and when we move a significant distance (10 kilometers) When a location is available—either at the start or based on movement—we retrieve the National Weather Service data via our updateForecast() method:
private void updateForecast(Location loc) {
String url=String.format(format, loc.getLatitude(),
loc.getLongitude());
HttpGet getMethod=new HttpGet(url);
try {
ResponseHandler<String> responseHandler=new BasicResponseHandler();
String responseBody=client.execute(getMethod,
responseHandler);
buildForecasts(responseBody);
Trang 3String page=generatePage();
browser.loadDataWithBaseURL(null, page, "text/html",
"UTF-8", null);
}
catch (Throwable t) {
Toast
makeText(this, "Request failed: "+t.toString(), 4000)
show();
}
}
The updateForecast() method takes a Location as a parameter, obtained from the
location update process For now, all you need to know is that Location sports
getLatitude() and getLongitude() methods, which return the latitude and longitude of
the device’s position, respectively
We hold the URL to the National Weather Service XML in a string resource, and pour in
the latitude and longitude at runtime Given our HttpClient object created in
onCreate(), we populate an HttpGet with that customized URL, and then execute that
method Given the resulting XML from the REST service, we build the forecast HTML
page, as described next, and pour that into the WebKit widget If the HttpClient blows
up with an exception, we provide that error as a Toast
Parsing Responses
The response you get will be formatted using some system—HTML, XML, JSON, or
whatever It is up to you, of course, to pick out the information you need and do
something useful with it In the case of the WeatherDemo, we need to extract the forecast
time, temperature, and icon (indicating sky conditions and precipitation), and generate
an HTML page from it
Android includes three XML parsers: the traditional W3C DOM parser (org.w3c.dom), a
SAX parser (org.xml.sax), and the XML pull parser (discussed in Chapter 20) It also has
a JSON parser (org.json)
You are also welcome to use third-party Java code, where possible, to handle other
formats, such as a dedicated RSS/Atom parser for a feed reader The use of third-party
Java code is discussed in Chapter 24
For WeatherDemo, we use the W3C DOM parser in our buildForecasts() method:
void buildForecasts(String raw) throws Exception {
DocumentBuilder builder=DocumentBuilderFactory
.newInstance()
.newDocumentBuilder();
Document doc=builder.parse(new InputSource(new StringReader(raw)));
NodeList times=doc.getElementsByTagName("start-valid-time");
for (int i=0;i<times.getLength();i++) {
Element time=(Element)times.item(i);
Forecast forecast=new Forecast();
Trang 4forecasts.add(forecast);
forecast.setTime(time.getFirstChild().getNodeValue());
}
NodeList temps=doc.getElementsByTagName("value");
for (int i=0;i<temps.getLength();i++) {
Element temp=(Element)temps.item(i);
Forecast forecast=forecasts.get(i);
forecast.setTemp(new Integer(temp.getFirstChild().getNodeValue()));
}
NodeList icons=doc.getElementsByTagName("icon-link");
for (int i=0;i<icons.getLength();i++) {
Element icon=(Element)icons.item(i);
Forecast forecast=forecasts.get(i);
forecast.setIcon(icon.getFirstChild().getNodeValue());
}
}
The National Weather Service XML format is curiously structured, relying heavily on sequential position in lists versus the more object-oriented style you find in formats like RSS or Atom That being said, we can take a few liberties and simplify the parsing somewhat, taking advantage of the fact that the elements we want (start-valid-time for the forecast time, value for the temperature, and icon-link for the icon URL) are unique within the document
The HTML comes in as an InputStream and is fed into the DOM parser From there, we scan for the start-valid-time elements and populate a set of Forecast models using those start times Then we find the temperature value elements and icon-link URLs and fill those in to the Forecast objects
In turn, the generatePage() method creates a rudimentary HTML table with the
forecasts:
String generatePage() {
StringBuffer bufResult=new StringBuffer("<html><body><table>");
bufResult.append("<tr><th width=\"50%\">Time</th>"+
"<th>Temperature</th><th>Forecast</th></tr>");
for (Forecast forecast : forecasts) {
bufResult.append("<tr><td align=\"center\">");
bufResult.append(forecast.getTime());
bufResult.append("</td><td align=\"center\">");
bufResult.append(forecast.getTemp());
bufResult.append("</td><td><img src=\"");
bufResult.append(forecast.getIcon());
bufResult.append("\"></td></tr>");
}
Trang 5bufResult.append("</table></body></html>");
return(bufResult.toString());
}
The result looks like Figure 25–1
Figure 25–1 The WeatherDemo sample application
Stuff to Consider
If you need to use Secure Sockets Layer (SSL) protocol, bear in mind that the default
HttpClient setup does not include SSL support Mostly, this is because you need to
decide how to handle SSL certificate presentation Do you blindly accept all certificates,
even self-signed or expired ones? Or do you want to ask the users if they really want to
use some strange certificates?
Similarly, the HttpClient component, by default, is designed for single-threaded use If
you will be using HttpClient from a service or some other place where multiple threads
might be an issue, you can readily set up HttpClient to support multiple threads
For these sorts of topics, you are best served by checking out the HttpClient web site
for documentation and support