11. Using Android Networking APIs

Applications written with networking components are far more dynamic and content-rich than those that are not. Applications leverage the network for a variety of reasons: to deliver fresh and updated content, to enable social networking features of an otherwise standalone application, to offload heavy processing to high-powered servers, and to enable data storage beyond what the user can achieve on the device.

Those accustomed to Java networking will find the java.net package familiar. There are also some helpful Android utility classes for various types of network operations and protocols. This chapter focuses on Hypertext Transfer Protocol (HTTP), the most common protocol for networked mobile applications.

Understanding Mobile Networking Fundamentals

Networking on the Android platform is standardized, using a combination of powerful yet familiar technologies and libraries such as java.net. Network implementation is generally straightforward, but mobile application developers need to plan for less stable connectivity than one might expect in a home or office network setting—connectivity depends on the location of the users and their devices. Users demand stable, responsive applications. This means that you must take extra care when designing network-enabled applications. Luckily, the Android SDK provides a number of tools and classes for ensuring just that.


Image Warning

Recall that developers must agree to a number of network best practices as part of the Android Software Development Kit License Agreement. If you plan to use network support in your application, you might want to review these contractual points to ensure that your application complies with the agreement.


Understanding Strict Mode with Networking

As discussed in Chapter 1, “Threading and Asynchronous Processing,” strict mode is a method that developers can use to detect operations performed on the main thread that should not be there. API Level 11 expanded upon strict mode in ways that impact networking code. By default, if you perform network operations on the main thread, your application throws an exception, specifically android.os.NetworkOnMainThreadException. The way to avoid this is to use proper coding techniques and put all networking operations on a thread other than the main thread. We show you how later in this chapter, or you can review Chapter 1 for the basics.

If you’re not writing a production application and want to run some quick network code without wiring up a full thread, you can disable the crashing and simply flash the screen instead (on API Level 11) or log the mistakes. You can also call the permitAll() method to skip strict mode entirely. This is not recommended for production applications.

On Android 4 and later devices, a Developer options setting screen is available to turn on and off the screen flashing with strict mode. However, this will not disable exceptions for networking activity.

Image Accessing the Internet (HTTP)

The most common way to transfer data to and from the network is to use HTTP. You can use HTTP to encapsulate almost any type of data and to secure the data with Secure Sockets Layer (SSL), which can be important when you transmit data that falls under privacy requirements. Also, most common ports used by HTTP are typically open from the device networks.


Image Tip

Many of the code examples provided in this chapter are taken from the SimpleNetworking application. This source code for the SimpleNetworking application is provided for download on the book’s websites.


Reading Data from the Web

Reading data from the Web can be simple. For example, if all you need to do is read some data from a website and you have the web address of that data, you can leverage the URL class (available as part of the java.net package) to read a fixed amount of text from a file on a web server, like this:

import java.io.InputStream;
import java.net.URL;

// ...

URL text = new URL(
    "http://api.flickr.com/services/feeds/photos_public.gne" +
    "?id=26648248@N04&lang=en-us&format=atom");

InputStream isText = text.openStream();
byte[] bText = new byte[250];
int readSize = isText.read(bText);
Log.i("Net", "readSize = " + readSize);
Log.i("Net", "bText = "+ new String(bText));
isText.close();

First, a new URL object is created with the URL to the data we want to read. A stream is then opened to the URL resource. From there, we read the data and close the InputStream. Reading data from a server can be that simple.


Image Note

As we state in the book’s introduction, exception handling has been stripped from book code examples for readability. However, when it comes to networking code, you often need to add this handling for the code examples to compile. See the sample code provided on the book’s websites for examples of how to implement exception handling properly.


However, remember that because we work with a network resource, errors can be more common. Our device might not have network coverage; the server might be down for maintenance or disappear entirely; the URL might be invalid; and network users might experience long waits and timeouts.

This method might work in some instances—for example, when your application has lightweight, noncritical network features—but it’s not particularly elegant. In many cases, you might want to know more about the data before reading from it from the URL. For instance, you might want to know how big it is.

Finally, for networking to work in any Android application, permission is required. Your application needs to have the following statement in its AndroidManifest.xml file:

<uses-permission
    android:name="android.permission.INTERNET"/>

Using HttpURLConnection

We can use the HttpURLConnection object to do a little reconnaissance on our URL before we transfer too much data. HttpURLConnection retrieves some information about the resource referenced by the URL object, including HTTP status and header information.

Some of the information you can retrieve from the HttpURLConnection includes the length of the content, content type, and date-time information so that you can check to see whether the data changed since the last time you accessed the URL.

Here is a short example of how to use HttpURLConnection to query the same URL previously used:

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

// ...

URL text = new URL(
    "http://api.flickr.com/services/feeds/photos_public.gne?id=26648248@N04&lang=en-us&format=atom");
HttpURLConnection http =
    (HttpURLConnection)text.openConnection();
Log.i("Net", "length = " + http.getContentLength());
Log.i("Net", "respCode = " + http.getResponseCode());
Log.i("Net", "contentType = "+ http.getContentType());
Log.i("Net", "content = " + http.getContent());

The log lines demonstrate a few useful methods with the HttpURLConnection class. If the URL content is deemed appropriate, you can then call http.getInputStream() to get the same InputStream object as before. From there, reading from the network resource is the same, but more is known about the resource.

Parsing XML from the Network

A large portion of data transmitted among network resources is stored in a structured fashion in Extensible Markup Language (XML). In particular, RSS feeds are provided in a standardized XML format, and many web services provide data using these feeds. Android SDK provides a variety of XML utilities. Parsing XML from the network is similar to parsing an XML resource file or a raw file on the file system. Android provides a fast and efficient XML Pull Parser, which is a parser of choice for networked applications.

The following code demonstrates how to use the XML Pull Parser to read an XML file from flickr.com and extract specific data from within it. A TextView called status is assigned before this block of code is executed and displays the status of the parsing operation.

import java.net.URL;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

// ...

URL text = new URL(
    "http://api.flickr.com/services/feeds/photos_public.gne?id=26648248@N04&lang=en-us&format=atom");

XmlPullParserFactory parserCreator =
    XmlPullParserFactory.newInstance();
XmlPullParser parser = parserCreator.newPullParser();

parser.setInput(text.openStream(), null);

status.setText("Parsing...");
int parserEvent = parser.getEventType();
while (parserEvent != XmlPullParser.END_DOCUMENT) {
    switch(parserEvent) {
        case XmlPullParser.START_TAG:
            String tag = parser.getName();

            if (tag.compareTo("link") == 0) {
                String relType =
                    parser.getAttributeValue(null, "rel");

                if (relType.compareTo("enclosure") == 0 ) {
                    String encType =
                        parser.getAttributeValue(null, "type");

                    if (encType.startsWith("image/")) {
                        String imageSrc =
                            parser.getAttributeValue(null, "href");
                        Log.i("Net",
                            "image source = " + imageSrc);
                    }
                }
            }
            break;
        }
    parserEvent = parser.next();
}
status.setText("Done...");

After the URL is created, the next step is to retrieve an XmlPullParser instance from the XmlPullParserFactory. A Pull Parser has a main method that returns the next event. The events returned by a Pull Parser are similar to methods used in the implementation of a SAX parser handler class. Instead, though, the code is handled iteratively. This method is more efficient for mobile use.

In this example, the only event that we check for is the START_TAG event, signifying the beginning of an XML tag. Attribute values are queried and compared. This example looks specifically for image URLs in the XML from a flickr feed query. When found, a log entry is made.

You can check for the following XML Pull Parser events:

START_TAG: Returned when a new tag is found (that is, <tag>)

TEXT: Returned when text is found (that is, <tag>text</tag> where text has been found)

END_TAG: Returned when the end of tag is found (that is, </tag>)

END_DOCUMENT: Returned when the end of the XML file is reached

Additionally, the parser can be set to validate the input. Typically, parsing without validation is used when under constrained memory environments, such as a mobile environment. Compliant, non-validating parsing is the default for this XML Pull Parser.

Handling Network Operations Asynchronously

Networking operations can take an indefinite amount of time to complete and should not block the main UI thread. The style of networking presented so far causes the UI thread it runs on to block until the operation finishes. You must move network operations off of the main UI thread.

Offloading networking operations is straightforward using the AsyncTask class and the standard Java Thread class. You can find out more about both methods in Chapter 1.

Handling Network Operations with the AsyncTask class

The simplest way to handle asynchronous processing is with the AsyncTask class. The following code demonstrates an example implementation of AsyncTask to perform the same functionality as the code earlier off the UI thread:

private class ImageLoader extends
    AsyncTask<URL, String, String> {

@Override
protected String doInBackground(
    URL... params) {
    // just one param
    try {
        URL text = params[0];

        // ... parsing code {

        publishProgress(
            "imgCount = " + curImageCount);

        // ... end parsing code }

    }
    catch (Exception e ) {
        Log.e("Net",
            "Failed in parsing XML", e);
        return "Finished with failure.";
    }

    return "Done...";
}


protected void onCancelled() {
   Log.e("Net", "Async task Cancelled");
}

protected void onPostExecute(String result) {
    mStatus.setText(result);
}


protected void onPreExecute() {
     mStatus.setText("About to load URL");
}

protected void onProgressUpdate(
    String... values) {
    // just one value, please
    mStatus.setText(values[0]);
}}

When launched with the AsyncTask.execute() method, doInBackground() runs in a background thread while the other methods run on the UI thread. There is no need to manage a Handler or post a Runnable object to it. This simplifies coding and debugging.

Handling Network Operations with the Thread class

If you’re more comfortable working with traditional Java threads, you can use the Thread class instead. The following code demonstrates how to launch a new Thread that connects to a remote server, retrieves and parses some XML, and posts a response back to the UI thread to change a TextView:

import java.net.URL;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

// ...

new Thread() {
    public void run() {
        try {
            URL text = new URL(
                "http://api.flickr.com/services/feeds/photos_public.gne?id=26648248@N04&lang=en-us&format=atom");

            XmlPullParserFactory parserCreator =
               XmlPullParserFactory.newInstance();
            XmlPullParser parser =
               parserCreator.newPullParser();

            parser.setInput(text.openStream(), null);

            mHandler.post(new Runnable() {
                public void run() {
                    status.setText("Parsing...");
                }
            });

            int parserEvent = parser.getEventType();
            while (parserEvent !=
                XmlPullParser.END_DOCUMENT) {

                // Parsing code here ...

                parserEvent = parser.next();
            }

            mHandler.post(new Runnable() {
                public void run() {
                    status.setText("Done...");
                }
            });

        } catch (Exception e) {
            Log.e("Net", "Error in network call", e);
        }
    }
}.start();

For this example, an anonymous Thread object is reasonable. We create it and call its start() method immediately. However, now that the code runs on a separate thread, the user interface updates must be posted back to the main thread. This is done by using a Handler object on the main thread and creating Runnable objects that execute to call setText() on the TextView widget named status.

The rest of the code remains the same as in the previous examples. Executing both the parsing code and the networking code on a separate thread allows the user interface to continue to behave in a responsive fashion while the network and parsing operations are done behind the scenes, resulting in a smooth and friendly user experience. This also allows for handling of interim actions by the user, such as canceling the transfer. You can accomplish this by implementing the Thread to listen for certain events and check for certain flags.

Displaying Images from a Network Resource

Now that we have covered how you can use a separate thread to parse XML, let’s take our example a bit deeper and talk about working with non-primitive data types.

Continuing with the previous example of parsing for image locations from a flickr feed, let’s display some images from the feed. The following example reads the image data and displays it on the screen, demonstrating another way you can use network resources:

import java.io.InputStream;
import java.net.URL;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import android.os.Handler;

// ...

final String imageSrc =
    parser.getAttributeValue(null, "href");

final String currentTitle = new String(title);
imageThread.queueEvent(new Runnable() {
    public void run() {
        InputStream bmis;
        try {
            bmis = new URL(imageSrc).openStream();
            final Drawable image = new BitmapDrawable(
                BitmapFactory.decodeStream(bmis));
            mHandler.post(new Runnable() {
                public void run() {
                    imageSwitcher.setImageDrawable(image);
                    info.setText(currentTitle);
                }
            });
        } catch (Exception e) {
            Log.e("Net", "Failed to grab image", e);
        }
    }
});

You can find this block of code in the parser thread, as previously described. After the image source and title of the image have been determined, a new Runnable object is queued for execution on a separate image-handling thread. The thread is merely a queue that receives the anonymous Runnable object created here and executes it at least 10 seconds after the last one, resulting in a slideshow of the images from the feed.


Image Warning

Although the preceding code is sound for local resources and URLs, for sources over slow connections, it might not work properly. This is a known issue with the Android SDK and is caused by a buffering issue with loading large bitmaps over slow connections. There is a relatively straightforward workaround that you can find in the code provided for this chapter.


As with the first networking example, a new URL object is created and an InputStream retrieved from it. You need a Drawable object to assign to the ImageSwitcher. Then you use the BitmapFactory.decodeStream() method, which takes an InputStream.

Finally, from this Runnable object, which runs on a separate queuing thread, spacing out image drawing, another anonymous Runnable object posts back to the main thread to actually update the ImageSwitcher with the new image. Figure 11.1 shows what the screen might look like showing decoding status and displaying the current image.

Image

Figure 11.1. Screen showing a flickr image and decoding status of feed.

Although all this continues to happen while the feed from flickr is decoded, certain operations are slower than others. For instance, while the image is decoded or drawn on the screen, you can notice a distinct hesitation in the progress of the decoding. This is to be expected on current mobile devices because most have only a single thread of execution available for applications. You need to use careful design to provide a reasonably smooth and responsive experience to the user.

Retrieving Android Network Status

The Android SDK provides utilities for gathering information about the current state of the network. This is useful to determine whether a network connection is even available before trying to use a network resource. The ConnectivityManager class provides a number of methods to do this. The following code determines whether the mobile (cellular) network is available and connected. In addition, it determines the same for the Wi-Fi network:

import android.net.ConnectivityManager;
import android.net.NetworkInfo;

// ...

ConnectivityManager cm = (ConnectivityManager)
    getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni =
    cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiAvail = ni.isAvailable();
boolean isWifiConn = ni.isConnected();
ni = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileAvail = ni.isAvailable();
boolean isMobileConn = ni.isConnected();

status.setText("WiFi Avail = "+ isWifiAvail +
    " Conn = " + isWifiConn +
    " Mobile Avail = "+ isMobileAvail +
    " Conn = " + isMobileConn);

First, an instance of the ConnectivityManager object is retrieved with a call to the getSystemService() method, available as part of your application Context. Then this instance retrieves NetworkInfo objects for both TYPE_WIFI and TYPE_MOBILE (for the cellular network). These objects are queried for their availability but can also be queried at a more detailed status level to learn exactly what state of connection (or disconnection) the network is in. Figure 11.2 shows the typical output for the emulator in which the mobile network is simulated but Wi-Fi isn’t available.

Image

Figure 11.2. Network status with Wi-Fi turned off by the user.

If the network is available, this does not necessarily mean the server that the network resource is on is available. However, a call to the ConnectivityManager method requestRouteToHost() can answer this question. This way, the application can give the user better feedback when there are network problems.

For your application to read the status of the network, it needs explicit permission. The following statement is required to be in its AndroidManifest.xml file:

<uses-permission
    android:name="android.permission.ACCESS_NETWORK_STATE"/>


Image Tip

Use the emulator networking settings to simulate various types of cellular networks, from GSM to HSDPA (and unlimited) data rates. Additionally, you can control the latency of the network to be similar to that of the cellular networks. Although this is useful for testing how your application behaves in good conditions for the chosen network type, it can’t simulate the real behavior of the network out in the field when the user is in bad coverage, goes on an elevator, or is on a train rapidly losing and reacquiring network coverage. Only physical device testing can truly reveal these results.


Summary

Many applications use networking to enhance and improve the features they can provide to the user. However, a user’s network connectivity is not a guaranteed, always-available service. Application developers need to design and implement networking features carefully to ensure a stable and responsive application. Integrating networking features into your mobile application needs to be considered at the design level. Deciding how much networking support your application should contain is part of the application design process.

References and More Information

Android SDK documentation on java.net package:

http://d.android.com/reference/java/net/package-summary.html

Android SDK documentation on android.net package:

http://d.android.com/reference/android/net/package-summary.html

Android SDK documentation on Strict Mode class:

http://d.android.com/reference/android/os/StrictMode.html

XML pull parsing:

http://www.xmlpull.org/

Android SDK documentation on Android XML Pull Parser:

http://d.android.com/reference/org/xmlpull/v1/XmlPullParser.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.192.167