1page.title=Managing Network Usage 2parent.title=Performing Network Operations 3parent.link=index.html 4 5trainingnavtop=true 6 7previous.title=Connecting to the Network 8previous.link=connecting.html 9next.title=Parsing XML Data 10next.link=xml.html 11 12@jd:body 13 14<div id="tb-wrapper"> 15<div id="tb"> 16 17<h2>This lesson teaches you to</h2> 18 <ol> 19 <li><a href="#check-connection">Check a Device's Network Connection</a></li> 20 <li><a href="#manage-usage">Manage Network Usage</a></li> 21 <li><a href="#prefs">Implement a Preferences Activity</a></li> 22 <li><a href="#pref-change">Respond to Preference Changes</a></li> 23 <li><a href="#detect-changes">Detect Connection Changes</a></li> 24</ol> 25<h2>You should also read</h2> 26<ul> 27 <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li> 28 <li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li> 29 <li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li> 30</ul> 31 32<h2>Try it out</h2> 33 34<div class="download-box"> 35 <a href="{@docRoot}shareables/training/NetworkUsage.zip" 36class="button">Download the sample</a> 37 <p class="filename">NetworkUsage.zip</p> 38</div> 39 40</div> 41</div> 42 43<p>This lesson describes how to write applications that have fine-grained 44control over their usage of network resources. If your application performs a 45lot of network operations, you should provide user settings that allow users 46to control your app’s data habits, such as how often your app syncs data, 47whether to perform uploads/downloads only when on Wi-Fi, whether to use data 48while roaming, and so on. With these controls available to them, users are much 49less likely to disable your app’s access to background data when they approach their 50limits, because they can instead precisely control how much data your app 51uses.</p> 52 53<p>For general guidelines on how to write apps that minimize the battery life 54impact of downloads and network connections, see 55<a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a> 56and <a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a>. 57 58<h2 id="check-connection">Check a Device's Network Connection</h2> 59 60<p>A device can have various types of network connections. This lesson 61focuses on using either a Wi-Fi or a mobile network connection. For the full 62list of possible network types, see {@link android.net.ConnectivityManager}.<p> 63 64<p>Wi-Fi is typically faster. Also, mobile data is often metered, which can get 65expensive. 66A common strategy for apps is to only fetch large data 67if a Wi-Fi network is available.</p> 68 69<p>Before you perform network operations, it's good practice to check the state of 70network connectivity. Among other things, this could prevent your app from inadvertently using 71the wrong radio. If a network connection is unavailable, your application 72should respond gracefully. To check the network connection, you typically use 73the following classes:</p> 74 75<ul> 76 77 <li>{@link android.net.ConnectivityManager}: Answers queries about the state 78of network connectivity. It also notifies applications when network 79connectivity changes. </li> 80 81 <li>{@link android.net.NetworkInfo}: Describes the status of a network 82interface of a given type (currently either Mobile or Wi-Fi). 83 </li> 84 85</ul> 86 87 88 89<p>This code snippet tests network connectivity for Wi-Fi and mobile. It 90determines whether these network interfaces are available (that is, whether 91network connectivity is possible) and/or connected (that is, whether network 92connectivity exists and if it is possible to establish sockets and pass 93data): </p> 94 95<pre> 96private static final String DEBUG_TAG = "NetworkStatusExample"; 97... 98ConnectivityManager connMgr = (ConnectivityManager) 99 getSystemService(Context.CONNECTIVITY_SERVICE); 100NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 101boolean isWifiConn = networkInfo.isConnected(); 102networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 103boolean isMobileConn = networkInfo.isConnected(); 104Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn); 105Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn); 106</pre> 107 108<p>Note that you should not base decisions on whether a network is 109"available." You should always check {@link 110android.net.NetworkInfo#isConnected isConnected()} before performing network 111operations, since {@link android.net.NetworkInfo#isConnected isConnected()} 112handles cases like flaky mobile networks, airplane mode, and restricted 113background data.</p> 114 115<p>A more concise way of checking whether a network interface is available is as 116follows. The method {@link 117android.net.ConnectivityManager#getActiveNetworkInfo() getActiveNetworkInfo()} 118returns a {@link android.net.NetworkInfo} instance representing the first 119connected network interface it can find, or <code>null</code> if none of the 120interfaces is connected (meaning that an 121internet connection is not available):</p> 122 123<pre> 124public boolean isOnline() { 125 ConnectivityManager connMgr = (ConnectivityManager) 126 getSystemService(Context.CONNECTIVITY_SERVICE); 127 NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); 128 return (networkInfo != null && networkInfo.isConnected()); 129} </pre> 130 131<p>To query more fine-grained state you can use {@link 132android.net.NetworkInfo.DetailedState}, but this should seldom be necessary.</p> 133 134 135<h2 id="manage-usage">Manage Network Usage</h2> 136 137<p>You can implement a preferences activity that gives users explicit control 138over your app's usage of network resources. For 139example:</p> 140 141<ul> 142 143<li>You might allow users to upload videos only when the device is connected to a 144Wi-Fi network.</li> 145 146<li>You might sync (or not) depending on specific criteria such as network 147availability, time interval, and so on.</li> 148 149</ul> 150 151<p>To write an app that supports network access and managing 152network usage, your manifest must have the right permissions and 153intent filters. 154</p> 155 156<ul> 157 <li>The manifest excerpted below includes the following permissions: 158 <ul> 159 160 <li>{@link android.Manifest.permission#INTERNET 161android.permission.INTERNET}—Allows applications to open network 162sockets.</li> 163 164 <li>{@link android.Manifest.permission#ACCESS_NETWORK_STATE 165android.permission.ACCESS_NETWORK_STATE}—Allows applications to access 166information about networks.</li> 167 168 </ul> 169 </li> 170 171 <li>You can declare the intent filter for the 172{@link android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} action (introduced in 173Android 4.0) to indicate that your application defines an activity that offers 174options to control data usage. {@link 175android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} shows settings for managing 176the network data usage of a specific application. When your app has a settings activity 177that allows users to control network usage, you should declare this intent filter for that activity. 178In the sample application, this action is handled by the class 179<code>SettingsActivity</code>, which displays a preferences UI to let users 180decide when to download a feed.</li> 181 182</ul> 183 184 185<pre> 186<?xml version="1.0" encoding="utf-8"?> 187<manifest xmlns:android="http://schemas.android.com/apk/res/android" 188 package="com.example.android.networkusage" 189 ...> 190 191 <uses-sdk android:minSdkVersion="4" 192 android:targetSdkVersion="14" /> 193 194 <uses-permission android:name="android.permission.INTERNET" /> 195 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 196 197 <application 198 ...> 199 ... 200 <activity android:label="SettingsActivity" android:name=".SettingsActivity"> 201 <intent-filter> 202 <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> 203 <category android:name="android.intent.category.DEFAULT" /> 204 </intent-filter> 205 </activity> 206 </application> 207</manifest> 208</pre> 209 210<h2 id="prefs">Implement a Preferences Activity</h2> 211 212<p>As you can see in the manifest excerpt above, the sample app's activity 213<code>SettingsActivity</code> has an intent filter for the {@link 214android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} action. 215<code>SettingsActivity</code> is a subclass of {@link 216android.preference.PreferenceActivity}. It displays a preferences screen 217(shown in figure 1) that 218lets users specify the following:</p> 219 220<ul> 221 222 <li>Whether to display summaries for each XML feed entry, or just a link for 223each entry.</li> 224 225 <li>Whether to download the XML feed if any network connection is available, 226or only if Wi-Fi is available.</li> 227 228</ul> 229 230<img src="{@docRoot}images/training/basics/network-settings1.png" alt="Preferences panel" /> 231 232<img src="{@docRoot}images/training/basics/network-settings2.png" alt="Setting a network preference" /> 233<p class="img-caption"><strong>Figure 1.</strong> Preferences activity.</p> 234 235<p>Here is <code>SettingsActivity</code>. Note that it implements 236{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener OnSharedPreferenceChangeListener}. 237When a user changes a preference, it fires 238{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged onSharedPreferenceChanged()}, 239which sets {@code refreshDisplay} to true. This causes the display to refresh when the user 240returns to the main activity:</p> 241 242<pre>public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener { 243 244 @Override 245 protected void onCreate(Bundle savedInstanceState) { 246 super.onCreate(savedInstanceState); 247 248 // Loads the XML preferences file 249 addPreferencesFromResource(R.xml.preferences); 250 } 251 252 @Override 253 protected void onResume() { 254 super.onResume(); 255 256 // Registers a listener whenever a key changes 257 getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 258 } 259 260 @Override 261 protected void onPause() { 262 super.onPause(); 263 264 // Unregisters the listener set in onResume(). 265 // It's best practice to unregister listeners when your app isn't using them to cut down on 266 // unnecessary system overhead. You do this in onPause(). 267 getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); 268 } 269 270 // When the user changes the preferences selection, 271 // onSharedPreferenceChanged() restarts the main activity as a new 272 // task. Sets the refreshDisplay flag to "true" to indicate that 273 // the main activity should update its display. 274 // The main activity queries the PreferenceManager to get the latest settings. 275 276 @Override 277 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 278 // Sets refreshDisplay to true so that when the user returns to the main 279 // activity, the display refreshes to reflect the new settings. 280 NetworkActivity.refreshDisplay = true; 281 } 282}</pre> 283 284<h2 id="pref-change">Respond to Preference Changes</h2> 285 286<p>When the user changes preferences in the settings screen, it typically has 287consequences for the app's behavior. In this snippet, the app checks the 288preferences settings in {@code onStart()}. if there is a match between the setting and 289the device's network connection (for example, if the setting is {@code "Wi-Fi"} and the 290device has a Wi-Fi connection), the app downloads the feed and refreshes the 291display.</p> 292 293<pre> 294public class NetworkActivity extends Activity { 295 public static final String WIFI = "Wi-Fi"; 296 public static final String ANY = "Any"; 297 private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest"; 298 299 // Whether there is a Wi-Fi connection. 300 private static boolean wifiConnected = false; 301 // Whether there is a mobile connection. 302 private static boolean mobileConnected = false; 303 // Whether the display should be refreshed. 304 public static boolean refreshDisplay = true; 305 306 // The user's current network preference setting. 307 public static String sPref = null; 308 309 // The BroadcastReceiver that tracks network connectivity changes. 310 private NetworkReceiver receiver = new NetworkReceiver(); 311 312 @Override 313 public void onCreate(Bundle savedInstanceState) { 314 super.onCreate(savedInstanceState); 315 316 // Registers BroadcastReceiver to track network connection changes. 317 IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); 318 receiver = new NetworkReceiver(); 319 this.registerReceiver(receiver, filter); 320 } 321 322 @Override 323 public void onDestroy() { 324 super.onDestroy(); 325 // Unregisters BroadcastReceiver when app is destroyed. 326 if (receiver != null) { 327 this.unregisterReceiver(receiver); 328 } 329 } 330 331 // Refreshes the display if the network connection and the 332 // pref settings allow it. 333 334 @Override 335 public void onStart () { 336 super.onStart(); 337 338 // Gets the user's network preference settings 339 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); 340 341 // Retrieves a string value for the preferences. The second parameter 342 // is the default value to use if a preference value is not found. 343 sPref = sharedPrefs.getString("listPref", "Wi-Fi"); 344 345 updateConnectedFlags(); 346 347 if(refreshDisplay){ 348 loadPage(); 349 } 350 } 351 352 // Checks the network connection and sets the wifiConnected and mobileConnected 353 // variables accordingly. 354 public void updateConnectedFlags() { 355 ConnectivityManager connMgr = (ConnectivityManager) 356 getSystemService(Context.CONNECTIVITY_SERVICE); 357 358 NetworkInfo activeInfo = connMgr.getActiveNetworkInfo(); 359 if (activeInfo != null && activeInfo.isConnected()) { 360 wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI; 361 mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE; 362 } else { 363 wifiConnected = false; 364 mobileConnected = false; 365 } 366 } 367 368 // Uses AsyncTask subclass to download the XML feed from stackoverflow.com. 369 public void loadPage() { 370 if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) 371 || ((sPref.equals(WIFI)) && (wifiConnected))) { 372 // AsyncTask subclass 373 new DownloadXmlTask().execute(URL); 374 } else { 375 showErrorPage(); 376 } 377 } 378... 379 380}</pre> 381 382<h2 id="detect-changes">Detect Connection Changes</h2> 383 384<p>The final piece of the puzzle is the {@link 385android.content.BroadcastReceiver} subclass, <code>NetworkReceiver</code>. When 386the device's network connection changes, <code>NetworkReceiver</code> intercepts 387the action {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION}, 388determines what the network connection status is, and sets the flags 389<code>wifiConnected</code> and <code>mobileConnected</code> to true/false 390accordingly. The upshot is that the next time the user returns to the app, the 391app will only download the latest feed and update the display if 392<code>NetworkActivity.refreshDisplay</code> is set to <code>true</code>.</p> 393 394<p>Setting up a BroadcastReceiver that gets called unnecessarily can be a 395drain on system resources. 396The sample application registers the 397{@link android.content.BroadcastReceiver} {@code NetworkReceiver} in 398{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}, 399and it unregisters it in 400{@link android.app.Activity#onDestroy onDestroy()}. This is more lightweight 401than declaring a {@code <receiver>} in the manifest. When you declare a 402{@code <receiver>} in the manifest, it can wake up your app at any time, 403even if you haven't run it for weeks. By registering and unregistering 404{@code NetworkReceiver} within the main activity, you ensure that the app won't 405be woken up after the user leaves the app. 406If you do declare a {@code <receiver>} in the manifest and you know exactly 407where you need it, you can use 408{@link android.content.pm.PackageManager#setComponentEnabledSetting setComponentEnabledSetting()} 409to enable and disable it as appropriate.</p> 410 411<p>Here is <code>NetworkReceiver</code>:</p> 412 413<pre>public class NetworkReceiver extends BroadcastReceiver { 414 415@Override 416public void onReceive(Context context, Intent intent) { 417 ConnectivityManager conn = (ConnectivityManager) 418 context.getSystemService(Context.CONNECTIVITY_SERVICE); 419 NetworkInfo networkInfo = conn.getActiveNetworkInfo(); 420 421 // Checks the user prefs and the network connection. Based on the result, decides whether 422 // to refresh the display or keep the current display. 423 // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection. 424 if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { 425 // If device has its Wi-Fi connection, sets refreshDisplay 426 // to true. This causes the display to be refreshed when the user 427 // returns to the app. 428 refreshDisplay = true; 429 Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show(); 430 431 // If the setting is ANY network and there is a network connection 432 // (which by process of elimination would be mobile), sets refreshDisplay to true. 433 } else if (ANY.equals(sPref) && networkInfo != null) { 434 refreshDisplay = true; 435 436 // Otherwise, the app can't download content--either because there is no network 437 // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there 438 // is no Wi-Fi connection. 439 // Sets refreshDisplay to false. 440 } else { 441 refreshDisplay = false; 442 Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show(); 443 } 444}</pre> 445 446