1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
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 = "NetworkStatusExample";
...
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
"available." 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 of 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}—Allows applications to open network
sockets.</li>
<li>{@link android.Manifest.permission#ACCESS_NETWORK_STATE
android.permission.ACCESS_NETWORK_STATE}—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>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.networkusage"
...>
<uses-sdk android:minSdkVersion="4"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
...>
...
<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>
</application>
</manifest>
</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 {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Loads the XML preferences file
addPreferencesFromResource(R.xml.preferences);
}
@Override
protected void onResume() {
super.onResume();
// Registers a listener whenever a key changes
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@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.
@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();
@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);
}
@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.
@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 <receiver>} in the manifest. When you declare a
{@code <receiver>} 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 <receiver>} 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 {
@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>
|