Smaple - NetworkUsage
- Downloads an XML feed from StackOverflow.com for the most recent posts tagged "android".
- Parses the XML feed, combines feed elements with HTML markup, and displays the resulting HTML in the UI.
- Lets users control their network data usage through a settings UI. Users can choose to fetch the feed when any network connection is available, or only when a Wi-Fi connection is available.
- Detects when there is a change in the device's connection status and responds accordingly. For example, if the device loses its network connection, the app will not attempt to download the feed.
1. Manifest
- Permissions
- INTERNET
- ACCESS_NETWORK_STATE
- Activity
<activity android:label="SettingsActivity" android:name=".SettingsActivity">
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
2. SettingsActivity extends PreferenceActivity
implements OnSharedPreferenceChangeListener
- onCreate()
// Loads the XML preferences file.
addPreferencesFromResource(R.xml.preferences);
- onResume()
// Registers a callback to be invoked whenever a user changes a preference.
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
- onPause()
// 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);
- overrides onSharedPreferenceChanged()
// 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;
3. preferences.xml
- ListPreference
android:key="listPref"
android:defaultValue="Wi-Fi"
android:entries="@array/listArray"
android:entryValues="@array/listValues"
<resources>
<string-array name="listArray">
<item>Only when on Wi-Fi</item>
<item>On any network</item>
</string-array>
<string-array name="listValues">
<item>Wi-Fi</item>
<item>Any</item>
</string-array>
</resources>
- CheckBoxPreference
4. NetworkActivity
- onCreate()
// Register BroadcastReceiver to track connection changes.
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
receiver = new NetworkReceiver();
this.registerReceiver(receiver, filter);
- onStart()
Refreshes the display if the network connection and the pref settings allow it. - updateConnectedFlags()
- loadPage()
if (refreshDisplay) {
loadPage();
}
onDestroy()
unregisterReceiver
updateConnectedFlags()
Checks the network connection and sets the wifiConnected and mobileConnected variables accordingly.
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;}
-
loadPage()
// Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
// This avoids UI lock up. To prevent network operations from
// causing a delay that results in a poor user experience, always perform
// network operations on a separate thread from the UI.
Uses a AsyncTask subclass
new DownloadXmlTask().execute(URL);
- DownloadXmlTask extends AsyncTask<String, Void, String>
- doInBackground(String... urls)
return loadXmlFromNetwork(urls[0]);
- onPostExecute(String result)
myWebView.loadData(result, "text/html", null);
- loadXmlFromNetwork(String urlString)
// Uploads XML from stackoverflow.com, parses it, and combines it with
// HTML markup. Returns HTML string.
StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
Checks whether the user set the preference to include summary text.
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean pref = sharedPrefs.getBoolean("summaryPref", false);
Downloads & Parses
stream = downloadUrl(urlString);
entries = stackOverflowXmlParser.parse(stream);
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("<p><a href='");
htmlString.append(entry.link);
htmlString.append("'>" + entry.title + "</a></p>");
// If the user set the preference to include summary text,
// adds it to the display.
if (pref) {
htmlString.append(entry.summary);
}
}
downloadUrl(String urlString)
Given a string representation of a URL, sets up a connection and gets an input stream.-
NetworkReceiver extends BroadcastReceiver
overrides onReceive()
// 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();}
5. StackOverflowXmlParser
- Instantiate the Parser.
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();
}
}
- readFeed()
- Start with tag "feed"
- Look for tag "entry"
- Add "readEntry(parser)" to list<Entry>
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;
}
- readEntry()
Parses the contents of an entry.
If it encounters a title, summary, or link tag, hands them off to their respective "read" 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);
}
- read method & skip()
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;
}
}
}
if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it finds the matching END_TAG (as indicated by the value of "depth" being 0).