summaryrefslogtreecommitdiffstats
path: root/docs/html/training/basics/network-ops
diff options
context:
space:
mode:
authorScott Main <smain@google.com>2012-06-21 17:14:39 -0700
committerScott Main <smain@google.com>2012-06-21 21:27:30 -0700
commit50e990c64fa23ce94efa76b9e72df7f8ec3cee6a (patch)
tree52605cd25e01763596477956963fabcd087054b0 /docs/html/training/basics/network-ops
parenta2860267cad115659018d636bf9203a644c680a7 (diff)
downloadframeworks_base-50e990c64fa23ce94efa76b9e72df7f8ec3cee6a.zip
frameworks_base-50e990c64fa23ce94efa76b9e72df7f8ec3cee6a.tar.gz
frameworks_base-50e990c64fa23ce94efa76b9e72df7f8ec3cee6a.tar.bz2
Massive clobber of all HTML files in developer docs for new site design
Change-Id: Idc55a0b368c1d2c1e7d4999601b739dd57f08eb3
Diffstat (limited to 'docs/html/training/basics/network-ops')
-rw-r--r--docs/html/training/basics/network-ops/connecting.jd284
-rw-r--r--docs/html/training/basics/network-ops/index.jd73
-rw-r--r--docs/html/training/basics/network-ops/managing.jd445
-rw-r--r--docs/html/training/basics/network-ops/xml.jd555
4 files changed, 1357 insertions, 0 deletions
diff --git a/docs/html/training/basics/network-ops/connecting.jd b/docs/html/training/basics/network-ops/connecting.jd
new file mode 100644
index 0000000..f70cf58
--- /dev/null
+++ b/docs/html/training/basics/network-ops/connecting.jd
@@ -0,0 +1,284 @@
+page.title=Connecting to the Network
+parent.title=Performing Network Operations
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Managing Network Usage
+next.link=managing.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#http-client">Choose an HTTP Client</a></li>
+ <li><a href="#connection">Check the Network Connection</a></li>
+ <li><a href="#AsyncTask">Perform Network Operations on a Separate Thread</a></li>
+ <li><a href="#download">Connect and Download Data</a></li>
+ <li><a href="#stream">Convert the InputStream to a String</a></li>
+
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+ <li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li>
+ <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li>
+ <li><a href="{@docRoot}guide/components/fundamentals.html">Application Fundamentals</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>This lesson shows you how to implement a simple application that connects to
+the network. It explains some of the best practices you should follow in
+creating even the simplest network-connected app.</p>
+
+<p>Note that to perform the network operations described in this lesson, your
+application manifest must include the following permissions:</p>
+
+<pre>&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&gt;
+&lt;uses-permission android:name=&quot;android.permission.ACCESS_NETWORK_STATE&quot; /&gt;</pre>
+
+
+
+<h2 id="http-client">Choose an HTTP Client</h2>
+
+<p>Most network-connected Android apps use HTTP to send and receive data.
+Android includes two HTTP clients: {@link java.net.HttpURLConnection} and Apache
+ {@link org.apache.http.client.HttpClient}. Both support HTTPS, streaming uploads and downloads, configurable
+timeouts, IPv6, and connection pooling. We recommend using {@link
+java.net.HttpURLConnection} for applications targeted at Gingerbread and higher. For
+more discussion of this topic, see the blog post <a
+href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html"
+>Android's HTTP Clients</a>.</p>
+
+<h2 id="connection">Check the Network Connection</h2>
+
+<p>Before your app attempts to connect to the network, it should check to see whether a
+network connection is available using
+{@link android.net.ConnectivityManager#getActiveNetworkInfo getActiveNetworkInfo()}
+and {@link android.net.NetworkInfo#isConnected isConnected()}.
+Remember, the device may be out of range of a
+network, or the user may have disabled both Wi-Fi and mobile data access.
+For more discussion of this topic, see the lesson <a
+href="{@docRoot}training/network-ops/managing.html">Managing Network
+Usage</a>.</p>
+
+<pre>
+public void myClickHandler(View view) {
+ ...
+ ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
+ if (networkInfo != null &amp;&amp; networkInfo.isConnected()) {
+ // fetch data
+ } else {
+ // display error
+ }
+ ...
+}</pre>
+
+<h2 id="AsyncTask">Perform Network Operations on a Separate Thread</h2>
+
+<p>Network operations can involve unpredictable delays. To prevent this from
+causing a poor user experience, always perform network operations on a separate
+thread from the UI. The {@link android.os.AsyncTask} class provides one of the
+simplest ways to fire off a new task from the UI thread. For more discussion of
+this topic, see the blog post <a
+href="http://android-developers.blogspot.com/2010/07/multithreading-for-
+performance.html">Multithreading For Performance</a>.</p>
+
+
+<p>In the following snippet, the <code>myClickHandler()</code> method invokes <code>new
+DownloadWebpageTask().execute(stringUrl)</code>. The
+<code>DownloadWebpageTask</code> class is a subclass of {@link
+android.os.AsyncTask}. <code>DownloadWebpageTask</code> implements the following
+{@link android.os.AsyncTask} methods:</p>
+
+ <ul>
+
+ <li>{@link android.os.AsyncTask#doInBackground doInBackground()} executes
+the method <code>downloadUrl()</code>. It passes the web page URL as a
+parameter. The method <code>downloadUrl()</code> fetches and processes the web
+page content. When it finishes, it passes back a result string.</li>
+
+ <li>{@link android.os.AsyncTask#onPostExecute onPostExecute()} takes the
+returned string and displays it in the UI.</li>
+
+
+ </ul>
+
+<pre>
+public class HttpExampleActivity extends Activity {
+ private static final String DEBUG_TAG = "HttpExample";
+ private EditText urlText;
+ private TextView textView;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ urlText = (EditText) findViewById(R.id.myUrl);
+ textView = (TextView) findViewById(R.id.myText);
+ }
+
+ // When user clicks button, calls AsyncTask.
+ // Before attempting to fetch the URL, makes sure that there is a network connection.
+ public void myClickHandler(View view) {
+ // Gets the URL from the UI's text field.
+ String stringUrl = urlText.getText().toString();
+ ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
+ if (networkInfo != null &amp;&amp; networkInfo.isConnected()) {
+ new DownloadWebpageText().execute(stringUrl);
+ } else {
+ textView.setText("No network connection available.");
+ }
+ }
+
+ // Uses AsyncTask to create a task away from the main UI thread. This task takes a
+ // URL string and uses it to create an HttpUrlConnection. Once the connection
+ // has been established, the AsyncTask downloads the contents of the webpage as
+ // an InputStream. Finally, the InputStream is converted into a string, which is
+ // displayed in the UI by the AsyncTask's onPostExecute method.
+ private class DownloadWebpageText extends AsyncTask<String, Void, String> {
+ &#64;Override
+ protected String doInBackground(String... urls) {
+
+ // params comes from the execute() call: params[0] is the url.
+ try {
+ return downloadUrl(urls[0]);
+ } catch (IOException e) {
+ return "Unable to retrieve web page. URL may be invalid.";
+ }
+ }
+ // onPostExecute displays the results of the AsyncTask.
+ &#64;Override
+ protected void onPostExecute(String result) {
+ textView.setText(result);
+ }
+ }
+ ...
+}</pre>
+
+<p>The sequence of events in this snippet is as follows:</p>
+<ol>
+
+ <li>When users click the button that invokes {@code myClickHandler()},
+ the app passes
+the specified URL to the {@link android.os.AsyncTask} subclass
+<code>DownloadWebpageTask</code>.</li>
+
+ <li>The {@link android.os.AsyncTask} method {@link
+android.os.AsyncTask#doInBackground doInBackground()} calls the
+<code>downloadUrl()</code> method. </li>
+
+ <li>The <code>downloadUrl()</code> method takes a URL string as a parameter
+and uses it to create a {@link java.net.URL} object.</li>
+
+ <li>The {@link java.net.URL} object is used to establish an {@link
+java.net.HttpURLConnection}.</li>
+
+ <li>Once the connection has been established, the {@link
+java.net.HttpURLConnection} object fetches the web page content as an {@link
+java.io.InputStream}.</li>
+
+ <li>The {@link java.io.InputStream} is passed to the <code>readIt()</code>
+method, which converts the stream to a string.</li>
+
+ <li>Finally, the {@link android.os.AsyncTask}'s {@link
+android.os.AsyncTask#onPostExecute onPostExecute()} method displays the string
+in the main activity's UI.</li>
+
+</ol>
+
+ <h2 id="download">Connect and Download Data</h2>
+
+ <p>In your thread that performs your network transactions, you can use
+ {@link java.net.HttpURLConnection} to perform a {@code GET} and download your data.
+ After you call {@code connect()}, you can get an {@link java.io.InputStream} of the data
+ by calling {@code getInputStream()}.
+
+ <p>In the following snippet, the {@link android.os.AsyncTask#doInBackground
+doInBackground()} method calls the method <code>downloadUrl()</code>. The
+<code>downloadUrl()</code> method takes the given URL and uses it to connect to
+the network via {@link java.net.HttpURLConnection}. Once a connection has been
+established, the app uses the method <code>getInputStream()</code> to retrieve
+the data as an {@link java.io.InputStream}.</p>
+
+<pre>
+// Given a URL, establishes an HttpUrlConnection and retrieves
+// the web page content as a InputStream, which it returns as
+// a string.
+private String downloadUrl(String myurl) throws IOException {
+ InputStream is = null;
+ // Only display the first 500 characters of the retrieved
+ // web page content.
+ int len = 500;
+
+ try {
+ URL url = new URL(myurl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(10000 /* milliseconds */);
+ conn.setConnectTimeout(15000 /* milliseconds */);
+ conn.setRequestMethod("GET");
+ conn.setDoInput(true);
+ // Starts the query
+ conn.connect();
+ int response = conn.getResponseCode();
+ Log.d(DEBUG_TAG, "The response is: " + response);
+ is = conn.getInputStream();
+
+ // Convert the InputStream into a string
+ String contentAsString = readIt(is, len);
+ return contentAsString;
+
+ // Makes sure that the InputStream is closed after the app is
+ // finished using it.
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+}</pre>
+
+<p>Note that the method <code>getResponseCode()</code> returns the connection's
+<a href="http://www.w3.org/Protocols/HTTP/HTRESP.html">status code</a>. This is
+a useful way of getting additional information about the connection. A status
+code of 200 indicates success.</p>
+
+<h2 id="stream">Convert the InputStream to a String</h2>
+
+<p>An {@link java.io.InputStream} is a readable source of bytes. Once you get an {@link java.io.InputStream},
+it's common to decode or convert it into a
+target data type. For example, if you were downloading image data, you might
+decode and display it like this:</p>
+
+<pre>InputStream is = null;
+...
+Bitmap bitmap = BitmapFactory.decodeStream(is);
+ImageView imageView = (ImageView) findViewById(R.id.image_view);
+imageView.setImageBitmap(bitmap);
+</pre>
+
+<p>In the example shown above, the {@link java.io.InputStream} represents the text of a
+web page. This is how the example converts the {@link java.io.InputStream} to
+a string so that the activity can display it in the UI:</p>
+
+<pre>// Reads an InputStream and converts it to a String.
+public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
+ Reader reader = null;
+ reader = new InputStreamReader(stream, "UTF-8");
+ char[] buffer = new char[len];
+ reader.read(buffer);
+ return new String(buffer);
+}</pre>
+
+
+
diff --git a/docs/html/training/basics/network-ops/index.jd b/docs/html/training/basics/network-ops/index.jd
new file mode 100644
index 0000000..b213c03
--- /dev/null
+++ b/docs/html/training/basics/network-ops/index.jd
@@ -0,0 +1,73 @@
+page.title=Performing Network Operations
+
+trainingnavtop=true
+startpage=true
+next.title=Connecting to the Network
+next.link=connecting.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 1.6 (API level 4) or higher</li>
+ <li>A device that is able to connect to mobile and Wi-Fi networks</li>
+</ul>
+
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+ <li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li>
+ <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li>
+</ul>
+
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/NetworkUsage.zip"
+class="button">Download the sample</a>
+ <p class="filename">NetworkUsage.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This class explains the basic tasks involved in connecting to the network,
+monitoring the network connection (including connection changes), and giving
+users control over an app's network usage. It also describes how to parse and
+consume XML data.</p>
+
+<p>This class includes a sample application that illustrates how to perform
+common network operations. You can download the sample (to the right) and use it
+as a source of reusable code for your own application.</p>
+
+<p>By going through these lessons, you'll have the
+fundamental building blocks for creating Android applications that download
+content and parse data efficiently, while minimizing network traffic.</p>
+
+
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><b><a href="connecting.html">Connecting to the Network</a></b></dt>
+
+ <dd>Learn how to connect to the network, choose an HTTP client, and perform
+network operations outside of the UI thread.</dd>
+
+ <dt><b><a href="managing.html">Managing Network Usage</a></b></dt>
+
+ <dd>Learn how to check a
+device's network connection, create a preferences UI for controlling network
+usage, and respond to connection changes.</dd>
+
+ <dt><b><a href="xml.html">Parsing XML Data</a></b></dt>
+ <dd>Learn how to parse and consume XML data.</dd>
+
+</dl>
+
diff --git a/docs/html/training/basics/network-ops/managing.jd b/docs/html/training/basics/network-ops/managing.jd
new file mode 100644
index 0000000..33cb195
--- /dev/null
+++ b/docs/html/training/basics/network-ops/managing.jd
@@ -0,0 +1,445 @@
+page.title=Managing Network Usage
+parent.title=Performing Network Operations
+parent.link=index.html
+
+trainingnavtop=true
+
+previous.title=Connecting to the Network
+previous.link=connecting.html
+next.title=Parsing XML Data
+next.link=xml.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+ <ol>
+ <li><a href="#check-connection">Check a Device's Network Connection</a></li>
+ <li><a href="#manage-usage">Manage Network Usage</a></li>
+ <li><a href="#prefs">Implement a Preferences Activity</a></li>
+ <li><a href="#pref-change">Respond to Preference Changes</a></li>
+ <li><a href="#detect-changes">Detect Connection Changes</a></li>
+</ol>
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+ <li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li>
+ <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/NetworkUsage.zip"
+class="button">Download the sample</a>
+ <p class="filename">NetworkUsage.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This lesson describes how to write applications that have fine-grained
+control over their usage of network resources. If your application performs a
+lot of network operations, you should provide user settings that allow users
+to control your app’s data habits, such as how often your app syncs data,
+whether to perform uploads/downloads only when on Wi-Fi, whether to use data
+while roaming, and so on. With these controls available to them, users are much
+less likely to disable your app’s access to background data when they approach their
+limits, because they can instead precisely control how much data your app
+uses.</p>
+
+<p>For general guidelines on how to write apps that minimize the battery life
+impact of downloads and network connections, see
+<a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a>
+and <a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a>.
+
+<h2 id="check-connection">Check a Device's Network Connection</h2>
+
+<p>A device can have various types of network connections. This lesson
+focuses on using either a Wi-Fi or a mobile network connection. For the full
+list of possible network types, see {@link android.net.ConnectivityManager}.<p>
+
+<p>Wi-Fi is typically faster. Also, mobile data is often metered, which can get
+expensive.
+A common strategy for apps is to only fetch large data
+if a Wi-Fi network is available.</p>
+
+<p>Before you perform network operations, it's good practice to check the state of
+network connectivity. Among other things, this could prevent your app from inadvertently using
+the wrong radio. If a network connection is unavailable, your application
+should respond gracefully. To check the network connection, you typically use
+the following classes:</p>
+
+<ul>
+
+ <li>{@link android.net.ConnectivityManager}: Answers queries about the state
+of network connectivity. It also notifies applications when network
+connectivity changes. </li>
+
+ <li>{@link android.net.NetworkInfo}: Describes the status of a network
+interface of a given type (currently either Mobile or Wi-Fi).
+ </li>
+
+</ul>
+
+
+
+<p>This code snippet tests network connectivity for Wi-Fi and mobile. It
+determines whether these network interfaces are available (that is, whether
+network connectivity is possible) and/or connected (that is, whether network
+connectivity exists and if it is possible to establish sockets and pass
+data): </p>
+
+<pre>
+private static final String DEBUG_TAG = &quot;NetworkStatusExample&quot;;
+...
+ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+boolean isWifiConn = networkInfo.isConnected();
+networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+boolean isMobileConn = networkInfo.isConnected();
+Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
+Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
+</pre>
+
+<p>Note that you should not base decisions on whether a network is
+&quot;available.&quot; You should always check {@link
+android.net.NetworkInfo#isConnected isConnected()} before performing network
+operations, since {@link android.net.NetworkInfo#isConnected isConnected()}
+handles cases like flaky mobile networks, airplane mode, and restricted
+background data.</p>
+
+<p>A more concise way of checking whether a network interface is available is as
+follows. The method {@link
+android.net.ConnectivityManager#getActiveNetworkInfo() getActiveNetworkInfo()}
+returns a {@link android.net.NetworkInfo} instance representing the first
+connected network interface it can find, or <code>null</code> if none if the
+interfaces is connected (meaning that an
+internet connection is not available):</p>
+
+<pre>
+public boolean isOnline() {
+ ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
+ return (networkInfo != null && networkInfo.isConnected());
+} </pre>
+
+<p>To query more fine-grained state you can use {@link
+android.net.NetworkInfo.DetailedState}, but this should seldom be necessary.</p>
+
+
+<h2 id="manage-usage">Manage Network Usage</h2>
+
+<p>You can implement a preferences activity that gives users explicit control
+over your app's usage of network resources. For
+example:</p>
+
+<ul>
+
+<li>You might allow users to upload videos only when the device is connected to a
+Wi-Fi network.</li>
+
+<li>You might sync (or not) depending on specific criteria such as network
+availability, time interval, and so on.</li>
+
+</ul>
+
+<p>To write an app that supports network access and managing
+network usage, your manifest must have the right permissions and
+intent filters.
+</p>
+
+<ul>
+ <li>The manifest excerpted below includes the following permissions:
+ <ul>
+
+ <li>{@link android.Manifest.permission#INTERNET
+android.permission.INTERNET}&mdash;Allows applications to open network
+sockets.</li>
+
+ <li>{@link android.Manifest.permission#ACCESS_NETWORK_STATE
+android.permission.ACCESS_NETWORK_STATE}&mdash;Allows applications to access
+information about networks.</li>
+
+ </ul>
+ </li>
+
+ <li>You can declare the intent filter for the
+{@link android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} action (introduced in
+Android 4.0) to indicate that your application defines an activity that offers
+options to control data usage. {@link
+android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} shows settings for managing
+the network data usage of a specific application. When your app has a settings activity
+that allows users to control network usage, you should declare this intent filter for that activity.
+In the sample application, this action is handled by the class
+<code>SettingsActivity</code>, which displays a preferences UI to let users
+decide when to download a feed.</li>
+
+</ul>
+
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.networkusage"
+ ...&gt;
+
+ &lt;uses-sdk android:minSdkVersion="4"
+ android:targetSdkVersion="14" /&gt;
+
+ &lt;uses-permission android:name="android.permission.INTERNET" /&gt;
+ &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /&gt;
+
+ &lt;application
+ ...&gt;
+ ...
+ &lt;activity android:label="SettingsActivity" android:name=".SettingsActivity"&gt;
+ &lt;intent-filter&gt;
+ &lt;action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /&gt;
+ &lt;category android:name="android.intent.category.DEFAULT" /&gt;
+ &lt;/intent-filter&gt;
+ &lt;/activity&gt;
+ &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
+
+<h2 id="prefs">Implement a Preferences Activity</h2>
+
+<p>As you can see in the manifest excerpt above, the sample app's activity
+<code>SettingsActivity</code> has an intent filter for the {@link
+android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} action.
+<code>SettingsActivity</code> is a subclass of {@link
+android.preference.PreferenceActivity}. It displays a preferences screen
+(shown in figure 1) that
+lets users specify the following:</p>
+
+<ul>
+
+ <li>Whether to display summaries for each XML feed entry, or just a link for
+each entry.</li>
+
+ <li>Whether to download the XML feed if any network connection is available,
+or only if Wi-Fi is available.</li>
+
+</ul>
+
+<img src="{@docRoot}images/training/basics/network-settings1.png" alt="Preferences panel" />
+
+<img src="{@docRoot}images/training/basics/network-settings2.png" alt="Setting a network preference" />
+<p class="img-caption"><strong>Figure 1.</strong> Preferences activity.</p>
+
+<p>Here is <code>SettingsActivity</code>. Note that it implements
+{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener OnSharedPreferenceChangeListener}.
+When a user changes a preference, it fires
+{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged onSharedPreferenceChanged()},
+which sets {@code refreshDisplay} to true. This causes the display to refresh when the user
+returns to the main activity:</p>
+
+<pre>public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Loads the XML preferences file
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+ &#64;Override
+ protected void onResume() {
+ super.onResume();
+
+ // Registers a listener whenever a key changes
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ &#64;Override
+ protected void onPause() {
+ super.onPause();
+
+ // Unregisters the listener set in onResume().
+ // It's best practice to unregister listeners when your app isn't using them to cut down on
+ // unnecessary system overhead. You do this in onPause().
+ getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ // When the user changes the preferences selection,
+ // onSharedPreferenceChanged() restarts the main activity as a new
+ // task. Sets the the refreshDisplay flag to "true" to indicate that
+ // the main activity should update its display.
+ // The main activity queries the PreferenceManager to get the latest settings.
+
+ &#64;Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // Sets refreshDisplay to true so that when the user returns to the main
+ // activity, the display refreshes to reflect the new settings.
+ NetworkActivity.refreshDisplay = true;
+ }
+}</pre>
+
+<h2 id="pref-change">Respond to Preference Changes</h2>
+
+<p>When the user changes preferences in the settings screen, it typically has
+consequences for the app's behavior. In this snippet, the app checks the
+preferences settings in {@code onStart()}. if there is a match between the setting and
+the device's network connection (for example, if the setting is {@code "Wi-Fi"} and the
+device has a Wi-Fi connection), the app downloads the feed and refreshes the
+display.</p>
+
+<pre>
+public class NetworkActivity extends Activity {
+ public static final String WIFI = "Wi-Fi";
+ public static final String ANY = "Any";
+ private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
+
+ // Whether there is a Wi-Fi connection.
+ private static boolean wifiConnected = false;
+ // Whether there is a mobile connection.
+ private static boolean mobileConnected = false;
+ // Whether the display should be refreshed.
+ public static boolean refreshDisplay = true;
+
+ // The user's current network preference setting.
+ public static String sPref = null;
+
+ // The BroadcastReceiver that tracks network connectivity changes.
+ private NetworkReceiver receiver = new NetworkReceiver();
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Registers BroadcastReceiver to track network connection changes.
+ IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+ receiver = new NetworkReceiver();
+ this.registerReceiver(receiver, filter);
+ }
+
+ &#64;Override
+ public void onDestroy() {
+ super.onDestroy();
+ // Unregisters BroadcastReceiver when app is destroyed.
+ if (receiver != null) {
+ this.unregisterReceiver(receiver);
+ }
+ }
+
+ // Refreshes the display if the network connection and the
+ // pref settings allow it.
+
+ &#64;Override
+ public void onStart () {
+ super.onStart();
+
+ // Gets the user's network preference settings
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Retrieves a string value for the preferences. The second parameter
+ // is the default value to use if a preference value is not found.
+ sPref = sharedPrefs.getString("listPref", "Wi-Fi");
+
+ updateConnectedFlags();
+
+ if(refreshDisplay){
+ loadPage();
+ }
+ }
+
+ // Checks the network connection and sets the wifiConnected and mobileConnected
+ // variables accordingly.
+ public void updateConnectedFlags() {
+ ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
+ if (activeInfo != null && activeInfo.isConnected()) {
+ wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
+ mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
+ } else {
+ wifiConnected = false;
+ mobileConnected = false;
+ }
+ }
+
+ // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
+ public void loadPage() {
+ if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
+ || ((sPref.equals(WIFI)) && (wifiConnected))) {
+ // AsyncTask subclass
+ new DownloadXmlTask().execute(URL);
+ } else {
+ showErrorPage();
+ }
+ }
+...
+
+}</pre>
+
+<h2 id="detect-changes">Detect Connection Changes</h2>
+
+<p>The final piece of the puzzle is the {@link
+android.content.BroadcastReceiver} subclass, <code>NetworkReceiver</code>. When
+the device's network connection changes, <code>NetworkReceiver</code> intercepts
+the action {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION},
+determines what the network connection status is, and sets the flags
+<code>wifiConnected</code> and <code>mobileConnected</code> to true/false
+accordingly. The upshot is that the next time the user returns to the app, the
+app will only download the latest feed and update the display if
+<code>NetworkActivity.refreshDisplay</code> is set to <code>true</code>.</p>
+
+<p>Setting up a BroadcastReceiver that gets called unnecessarily can be a
+drain on system resources.
+The sample application registers the
+{@link android.content.BroadcastReceiver} {@code NetworkReceiver} in
+{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()},
+and it unregisters it in
+{@link android.app.Activity#onDestroy onDestroy()}. This is more lightweight
+than declaring a {@code &lt;receiver&gt;} in the manifest. When you declare a
+{@code &lt;receiver&gt;} in the manifest, it can wake up your app at any time,
+even if you haven't run it for weeks. By registering and unregistering
+{@code NetworkReceiver} within the main activity, you ensure that the app won't
+be woken up after the user leaves the app.
+If you do declare a {@code &lt;receiver&gt;} in the manifest and you know exactly
+where you need it, you can use
+{@link android.content.pm.PackageManager#setComponentEnabledSetting setComponentEnabledSetting()}
+to enable and disable it as appropriate.</p>
+
+<p>Here is <code>NetworkReceiver</code>:</p>
+
+<pre>public class NetworkReceiver extends BroadcastReceiver {
+
+&#64;Override
+public void onReceive(Context context, Intent intent) {
+ ConnectivityManager conn = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = conn.getActiveNetworkInfo();
+
+ // Checks the user prefs and the network connection. Based on the result, decides whether
+ // to refresh the display or keep the current display.
+ // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
+ if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ // If device has its Wi-Fi connection, sets refreshDisplay
+ // to true. This causes the display to be refreshed when the user
+ // returns to the app.
+ refreshDisplay = true;
+ Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
+
+ // If the setting is ANY network and there is a network connection
+ // (which by process of elimination would be mobile), sets refreshDisplay to true.
+ } else if (ANY.equals(sPref) && networkInfo != null) {
+ refreshDisplay = true;
+
+ // Otherwise, the app can't download content--either because there is no network
+ // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
+ // is no Wi-Fi connection.
+ // Sets refreshDisplay to false.
+ } else {
+ refreshDisplay = false;
+ Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
+ }
+}</pre>
+
diff --git a/docs/html/training/basics/network-ops/xml.jd b/docs/html/training/basics/network-ops/xml.jd
new file mode 100644
index 0000000..b148257
--- /dev/null
+++ b/docs/html/training/basics/network-ops/xml.jd
@@ -0,0 +1,555 @@
+page.title=Parsing XML Data
+parent.title=Performing Network Operations
+parent.link=index.html
+
+trainingnavtop=true
+
+previous.title=Managing Network Usage
+previous.link=managing.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#choose">Choose a Parser</a></li>
+ <li><a href="#analyze">Analyze the Feed</a></li>
+ <li><a href="#instantiate">Instantiate the Parser</a></li>
+ <li><a href="#read">Read the Feed</a></li>
+ <li><a href="#parse">Parse XML</a></li>
+ <li><a href="#skip">Skip Tags You Don't Care About</a></li>
+ <li><a href="#consume">Consume XML Data</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/NetworkUsage.zip"
+class="button">Download the sample</a>
+ <p class="filename">NetworkUsage.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Extensible Markup Language (XML) is a set of rules for encoding documents in
+machine-readable form. XML is a popular format for sharing data on the internet.
+Websites that frequently update their content, such as news sites or blogs,
+often provide an XML feed so that external programs can keep abreast of content
+changes. Uploading and parsing XML data is a common task for network-connected
+apps. This lesson explains how to parse XML documents and use their data.</p>
+
+<h2 id="choose">Choose a Parser</h2>
+
+<p>We recommend {@link org.xmlpull.v1.XmlPullParser}, which is an efficient and
+maintainable way to parse XML on Android. Historically Android has had two
+implementations of this interface:</p>
+
+<ul>
+ <li><a href="http://kxml.sourceforge.net/"><code>KXmlParser</code></a>
+ via {@link org.xmlpull.v1.XmlPullParserFactory#newPullParser XmlPullParserFactory.newPullParser()}.
+ </li>
+ <li><code>ExpatPullParser</code>, via
+ {@link android.util.Xml#newPullParser Xml.newPullParser()}.
+ </li>
+</ul>
+
+<p>Either choice is fine. The
+example in this section uses <code>ExpatPullParser</code>, via
+{@link android.util.Xml#newPullParser Xml.newPullParser()}. </p>
+
+<h2 id="analyze">Analyze the Feed</h2>
+
+<p>The first step in parsing a feed is to decide which fields you're interested in.
+The parser extracts data for those fields and ignores the rest.</p>
+
+<p>Here is an excerpt from the feed that's being parsed in the sample app. Each
+post to <a href="http://stackoverflow.com">StackOverflow.com</a> appears in the
+feed as an <code>entry</code> tag that contains several nested tags:</p>
+
+<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot; xmlns:creativeCommons=&quot;http://backend.userland.com/creativeCommonsRssModule&quot; ...&quot;&gt;
+&lt;title type=&quot;text&quot;&gt;newest questions tagged android - Stack Overflow&lt;/title&gt;
+...
+ &lt;entry&gt;
+ ...
+ &lt;/entry&gt;
+ &lt;entry&gt;
+ &lt;id&gt;http://stackoverflow.com/q/9439999&lt;/id&gt;
+ &lt;re:rank scheme="http://stackoverflow.com"&gt;0&lt;/re:rank&gt;
+ &lt;title type="text"&gt;Where is my data file?&lt;/title&gt;
+ &lt;category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&amp;sort=newest/tags" term="android"/&gt;
+ &lt;category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&amp;sort=newest/tags" term="file"/&gt;
+ &lt;author&gt;
+ &lt;name&gt;cliff2310&lt;/name&gt;
+ &lt;uri&gt;http://stackoverflow.com/users/1128925&lt;/uri&gt;
+ &lt;/author&gt;
+ &lt;link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" /&gt;
+ &lt;published&gt;2012-02-25T00:30:54Z&lt;/published&gt;
+ &lt;updated&gt;2012-02-25T00:30:54Z&lt;/updated&gt;
+ &lt;summary type="html"&gt;
+ &lt;p&gt;I have an Application that requires a data file...&lt;/p&gt;
+
+ &lt;/summary&gt;
+ &lt;/entry&gt;
+ &lt;entry&gt;
+ ...
+ &lt;/entry&gt;
+...
+&lt;/feed&gt;</pre>
+
+<p>The sample app
+extracts data for the <code>entry</code> tag and its nested tags
+<code>title</code>, <code>link</code>, and <code>summary</code>.</p>
+
+
+<h2 id="instantiate">Instantiate the Parser</h2>
+
+<p>The next step is to
+instantiate a parser and kick off the parsing process. In this snippet, a parser
+is initialized to not process namespaces, and to use the provided {@link
+java.io.InputStream} as its input. It starts the parsing process with a call to
+{@link org.xmlpull.v1.XmlPullParser#nextTag() nextTag()} and invokes the
+<code>readFeed()</code> method, which extracts and processes the data the app is
+interested in:</p>
+
+<pre>public class StackOverflowXmlParser {
+ // We don't use namespaces
+ private static final String ns = null;
+
+ public List<Entry> parse(InputStream in) throws XmlPullParserException, IOException {
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(in, null);
+ parser.nextTag();
+ return readFeed(parser);
+ } finally {
+ in.close();
+ }
+ }
+ ...
+}</pre>
+
+<h2 id="read">Read the Feed</h2>
+
+<p>The <code>readFeed()</code> method does the actual work of processing the
+feed. It looks for elements tagged "entry" as a starting point for recursively
+processing the feed. If a tag isn't an {@code entry} tag, it skips it. Once the whole
+feed has been recursively processed, <code>readFeed()</code> returns a {@link
+java.util.List} containing the entries (including nested data members) it
+extracted from the feed. This {@link java.util.List} is then returned by the
+parser.</p>
+
+<pre>
+private List<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
+ List<Entry> entries = new ArrayList<Entry>();
+
+ parser.require(XmlPullParser.START_TAG, ns, "feed");
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ // Starts by looking for the entry tag
+ if (name.equals("entry")) {
+ entries.add(readEntry(parser));
+ } else {
+ skip(parser);
+ }
+ }
+ return entries;
+}</pre>
+
+
+<h2 id="parse">Parse XML</h2>
+
+
+<p>The steps for parsing an XML feed are as follows:</p>
+<ol>
+
+ <li>As described in <a href="#analyze">Analyze the Feed</a>, identify the tags you want to include in your app. This
+example extracts data for the <code>entry</code> tag and its nested tags
+<code>title</code>, <code>link</code>, and <code>summary</code>.</li>
+
+<li>Create the following methods:</p>
+
+<ul>
+
+<li>A "read" method for each tag you're interested in. For example,
+<code>readEntry()</code>, <code>readTitle()</code>, and so on. The parser reads
+tags from the input stream. When it encounters a tag named <code>entry</code>,
+<code>title</code>,
+<code>link</code> or <code>summary</code>, it calls the appropriate method
+for that tag. Otherwise, it skips the tag.
+</li>
+
+<li>Methods to extract data for each different type of tag and to advance the
+parser to the next tag. For example:
+<ul>
+
+<li>For the <code>title</code> and <code>summary</code> tags, the parser calls
+<code>readText()</code>. This method extracts data for these tags by calling
+<code>parser.getText()</code>.</li>
+
+<li>For the <code>link</code> tag, the parser extracts data for links by first
+determining if the link is the kind
+it's interested in. Then it uses <code>parser.getAttributeValue()</code> to
+extract the link's value.</li>
+
+<li>For the <code>entry</code> tag, the parser calls <code>readEntry()</code>.
+This method parses the entry's nested tags and returns an <code>Entry</code>
+object with the data members <code>title</code>, <code>link</code>, and
+<code>summary</code>.</li>
+
+</ul>
+</li>
+<li>A helper <code>skip()</code> method that's recursive. For more discussion of this topic, see <a href="#skip">Skip Tags You Don't Care About</a>.</li>
+</ul>
+
+ </li>
+</ol>
+
+<p>This snippet shows how the parser parses entries, titles, links, and summaries.</p>
+<pre>public static class Entry {
+ public final String title;
+ public final String link;
+ public final String summary;
+
+ private Entry(String title, String summary, String link) {
+ this.title = title;
+ this.summary = summary;
+ this.link = link;
+ }
+}
+
+// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
+// to their respective &quot;read&quot; methods for processing. Otherwise, skips the tag.
+private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
+ parser.require(XmlPullParser.START_TAG, ns, "entry");
+ String title = null;
+ String summary = null;
+ String link = null;
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ if (name.equals("title")) {
+ title = readTitle(parser);
+ } else if (name.equals("summary")) {
+ summary = readSummary(parser);
+ } else if (name.equals("link")) {
+ link = readLink(parser);
+ } else {
+ skip(parser);
+ }
+ }
+ return new Entry(title, summary, link);
+}
+
+// Processes title tags in the feed.
+private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, ns, "title");
+ String title = readText(parser);
+ parser.require(XmlPullParser.END_TAG, ns, "title");
+ return title;
+}
+
+// Processes link tags in the feed.
+private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String link = "";
+ parser.require(XmlPullParser.START_TAG, ns, "link");
+ String tag = parser.getName();
+ String relType = parser.getAttributeValue(null, "rel");
+ if (tag.equals("link")) {
+ if (relType.equals("alternate")){
+ link = parser.getAttributeValue(null, "href");
+ parser.nextTag();
+ }
+ }
+ parser.require(XmlPullParser.END_TAG, ns, "link");
+ return link;
+}
+
+// Processes summary tags in the feed.
+private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, ns, "summary");
+ String summary = readText(parser);
+ parser.require(XmlPullParser.END_TAG, ns, "summary");
+ return summary;
+}
+
+// For the tags title and summary, extracts their text values.
+private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String result = "";
+ if (parser.next() == XmlPullParser.TEXT) {
+ result = parser.getText();
+ parser.nextTag();
+ }
+ return result;
+}
+ ...
+}</pre>
+
+<h2 id="skip">Skip Tags You Don't Care About</h2>
+
+<p>One of the steps in the XML parsing described above is for the parser to skip tags it's not interested in. Here is the parser's <code>skip()</code> method:</p>
+
+<pre>
+private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ throw new IllegalStateException();
+ }
+ int depth = 1;
+ while (depth != 0) {
+ switch (parser.next()) {
+ case XmlPullParser.END_TAG:
+ depth--;
+ break;
+ case XmlPullParser.START_TAG:
+ depth++;
+ break;
+ }
+ }
+ }
+</pre>
+
+<p>This is how it works:</p>
+
+<ul>
+
+<li>It throws an exception if the current event isn't a
+<code>START_TAG</code>.</li>
+
+<li>It consumes the <code>START_TAG</code>, and all events up to and including
+the matching <code>END_TAG</code>.</li>
+
+<li>To make sure that it stops at the correct <code>END_TAG</code> and not at
+the first tag it encounters after the original <code>START_TAG</code>, it keeps
+track of the nesting depth.</li>
+
+</ul>
+
+<p>Thus if the current element has nested elements, the value of
+<code>depth</code> won't be 0 until the parser has consumed all events between
+the original <code>START_TAG</code> and its matching <code>END_TAG</code>. For
+example, consider how the parser skips the <code>&lt;author&gt;</code> element,
+which has 2 nested elements, <code>&lt;name&gt;</code> and
+<code>&lt;uri&gt;</code>:</p>
+
+<ul>
+
+<li>The first time through the <code>while</code> loop, the next tag the parser
+encounters after <code>&lt;author&gt;</code> is the <code>START_TAG</code> for
+<code>&lt;name&gt;</code>. The value for <code>depth</code> is incremented to
+2.</li>
+
+<li>The second time through the <code>while</code> loop, the next tag the parser
+encounters is the <code>END_TAG</code> <code>&lt;/name&gt;</code>. The value
+for <code>depth</code> is decremented to 1.</li>
+
+<li>The third time through the <code>while</code> loop, the next tag the parser
+encounters is the <code>START_TAG</code> <code>&lt;uri&gt;</code>. The value
+for <code>depth</code> is incremented to 2.</li>
+
+<li>The fourth time through the <code>while</code> loop, the next tag the parser
+encounters is the <code>END_TAG</code> <code>&lt;/uri&gt;</code>. The value for
+<code>depth</code> is decremented to 1.</li>
+
+<li>The fifth time and final time through the <code>while</code> loop, the next
+tag the parser encounters is the <code>END_TAG</code>
+<code>&lt;/author&gt;</code>. The value for <code>depth</code> is decremented to
+0, indicating that the <code>&lt;author&gt;</code> element has been successfully
+skipped.</li>
+
+</ul>
+
+<h2 id="consume">Consume XML Data</h2>
+
+<p>The example application fetches and parses the XML feed within an {@link
+android.os.AsyncTask}. This takes the processing off the main UI thread. When
+processing is complete, the app updates the UI in the main activity
+(<code>NetworkActivity</code>).</p>
+<p>In the excerpt shown below, the <code>loadPage()</code> method does the
+following:</p>
+
+<ul>
+
+ <li>Initializes a string variable with the URL for the XML feed.</li>
+
+ <li>If the user's settings and the network connection allow it, invokes
+<code>new DownloadXmlTask().execute(url)</code>. This instantiates a new
+<code>DownloadXmlTask</code> object ({@link android.os.AsyncTask} subclass) and
+runs its {@link android.os.AsyncTask#execute execute()} method, which downloads
+and parses the feed and returns a string result to be displayed in the UI.</li>
+
+</ul>
+<pre>
+public class NetworkActivity extends Activity {
+ public static final String WIFI = "Wi-Fi";
+ public static final String ANY = "Any";
+ private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
+
+ // Whether there is a Wi-Fi connection.
+ private static boolean wifiConnected = false;
+ // Whether there is a mobile connection.
+ private static boolean mobileConnected = false;
+ // Whether the display should be refreshed.
+ public static boolean refreshDisplay = true;
+ public static String sPref = null;
+
+ ...
+
+ // Uses AsyncTask to download the XML feed from stackoverflow.com.
+ public void loadPage() {
+
+ if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {
+ new DownloadXmlTask().execute(URL);
+ }
+ else if ((sPref.equals(WIFI)) && (wifiConnected)) {
+ new DownloadXmlTask().execute(URL);
+ } else {
+ // show error
+ }
+ }</pre>
+
+<p>The {@link android.os.AsyncTask} subclass shown below,
+<code>DownloadXmlTask</code>, implements the following {@link
+android.os.AsyncTask} methods:</p>
+
+ <ul>
+
+ <li>{@link android.os.AsyncTask#doInBackground doInBackground()} executes
+the method <code>loadXmlFromNetwork()</code>. It passes the feed URL as a
+parameter. The method <code>loadXmlFromNetwork()</code> fetches and processes
+the feed. When it finishes, it passes back a result string.</li>
+
+ <li>{@link android.os.AsyncTask#onPostExecute onPostExecute()} takes the
+returned string and displays it in the UI.</li>
+
+ </ul>
+
+<pre>
+// Implementation of AsyncTask used to download XML feed from stackoverflow.com.
+private class DownloadXmlTask extends AsyncTask&lt;String, Void, String&gt; {
+ &#64;Override
+ protected String doInBackground(String... urls) {
+ try {
+ return loadXmlFromNetwork(urls[0]);
+ } catch (IOException e) {
+ return getResources().getString(R.string.connection_error);
+ } catch (XmlPullParserException e) {
+ return getResources().getString(R.string.xml_error);
+ }
+ }
+
+ &#64;Override
+ protected void onPostExecute(String result) {
+ setContentView(R.layout.main);
+ // Displays the HTML string in the UI via a WebView
+ WebView myWebView = (WebView) findViewById(R.id.webview);
+ myWebView.loadData(result, "text/html", null);
+ }
+}</pre>
+
+ <p>Below is the method <code>loadXmlFromNetwork()</code> that is invoked from
+<code>DownloadXmlTask</code>. It does the following:</p>
+
+ <ol>
+
+ <li>Instantiates a <code>StackOverflowXmlParser</code>. It also creates variables for
+a {@link java.util.List} of <code>Entry</code> objects (<code>entries</code>), and
+<code>title</code>, <code>url</code>, and <code>summary</code>, to hold the
+values extracted from the XML feed for those fields.</li>
+
+ <li>Calls <code>downloadUrl()</code>, which fetches the feed and returns it as
+ an {@link java.io.InputStream}.</li>
+
+ <li>Uses <code>StackOverflowXmlParser</code> to parse the {@link java.io.InputStream}.
+ <code>StackOverflowXmlParser</code> populates a
+ {@link java.util.List} of <code>entries</code> with data from the feed.</li>
+
+ <li>Processes the <code>entries</code> {@link java.util.List},
+ and combines the feed data with HTML markup.</li>
+
+ <li>Returns an HTML string that is displayed in the main activity
+UI by the {@link android.os.AsyncTask} method {@link
+android.os.AsyncTask#onPostExecute onPostExecute()}.</li>
+
+</ol>
+
+<pre>
+// Uploads XML from stackoverflow.com, parses it, and combines it with
+// HTML markup. Returns HTML string.
+private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
+ InputStream stream = null;
+ // Instantiate the parser
+ StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
+ List&lt;Entry&gt; entries = null;
+ String title = null;
+ String url = null;
+ String summary = null;
+ Calendar rightNow = Calendar.getInstance();
+ DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");
+
+ // Checks whether the user set the preference to include summary text
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean pref = sharedPrefs.getBoolean("summaryPref", false);
+
+ StringBuilder htmlString = new StringBuilder();
+ htmlString.append("&lt;h3&gt;" + getResources().getString(R.string.page_title) + "&lt;/h3&gt;");
+ htmlString.append("&lt;em&gt;" + getResources().getString(R.string.updated) + " " +
+ formatter.format(rightNow.getTime()) + "&lt;/em&gt;");
+
+ try {
+ stream = downloadUrl(urlString);
+ entries = stackOverflowXmlParser.parse(stream);
+ // Makes sure that the InputStream is closed after the app is
+ // finished using it.
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+
+ // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
+ // Each Entry object represents a single post in the XML feed.
+ // This section processes the entries list to combine each entry with HTML markup.
+ // Each entry is displayed in the UI as a link that optionally includes
+ // a text summary.
+ for (Entry entry : entries) {
+ htmlString.append("&lt;p&gt;&lt;a href='");
+ htmlString.append(entry.link);
+ htmlString.append("'&gt;" + entry.title + "&lt;/a&gt;&lt;/p&gt;");
+ // If the user set the preference to include summary text,
+ // adds it to the display.
+ if (pref) {
+ htmlString.append(entry.summary);
+ }
+ }
+ return htmlString.toString();
+}
+
+// Given a string representation of a URL, sets up a connection and gets
+// an input stream.
+private InputStream downloadUrl(String urlString) throws IOException {
+ URL url = new URL(urlString);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(10000 /* milliseconds */);
+ conn.setConnectTimeout(15000 /* milliseconds */);
+ conn.setRequestMethod("GET");
+ conn.setDoInput(true);
+ // Starts the query
+ conn.connect();
+ InputStream stream = conn.getInputStream();
+}</pre>