am 4dc763c7: Merge "doc change: Android U lessons for audio and battery" into ics-mr0

* commit '4dc763c700f79e33030e96eefabf8e5047cb9cb1':
  doc change: Android U lessons for audio and battery
This commit is contained in:
Scott Main
2011-12-14 14:53:40 -08:00
committed by Android Git Automerger
9 changed files with 952 additions and 0 deletions

View File

@@ -0,0 +1,183 @@
page.title=Managing Audio Focus
parent.title=Managing Audio Playback and Focus
parent.link=index.html
trainingnavtop=true
previous.title=Controlling Your App's Volume and Playback
previous.link=volume-playback.html
next.title=Dealing with Audio Output Hardware
next.link=audio-output.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#RequestFocus">Request the Audio Focus</a></li>
<li><a href="#HandleFocusLoss">Handle the Loss of Audio Focus</a></li>
<li><a href="#DUCK">Duck!</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></li>
</ul>
</div>
</div>
<p>With multiple apps potentially playing audio it's important to think about how they should
interact. To avoid every music app playing at the same time, Android uses audio focus to moderate
audio playback&mdash;only apps that hold the audio focus should play audio.</p>
<p>Before your app starts playing audio it should request&mdash;and receive&mdash;the audio focus.
Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that
happens.</p>
<h2 id="RequestFocus">Request the Audio Focus</h2>
<p>Before your app starts playing any audio, it should hold the audio focus for the stream
it will be using. This is done with a call to {@link android.media.AudioManager#requestAudioFocus
requestAudioFocus()} which returns
{@link android.media.AudioManager#AUDIOFOCUS_REQUEST_GRANTED} if your request is successful.</p>
<p>You must specify which stream you're using and whether you expect to require transient or
permanent audio focus. Request transient focus when you expect to play audio for only a short time
(for example when playing navigation instructions). Request permanent audio focus when you
plan to play audio for the foreseeable future (for example, when playing music).</p>
<p>The following snippet requests permanent audio focus on the music audio stream. You should
request the audio focus immediately before you begin playback, such as when the user presses
play or the background music for the next game level begins.</p>
<pre>
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
// Start playback.
}
</pre>
<p>Once you've finished playback be sure to call {@link
android.media.AudioManager#abandonAudioFocus abandonAudioFocus()}. This notifies
the system that you no longer require focus and unregisters the associated {@link
android.media.AudioManager.OnAudioFocusChangeListener}. In the case of abandoning transient focus,
this allows any interupted app to continue playback.</p>
<pre>
// Abandon audio focus when playback complete
am.abandonAudioFocus(afChangeListener);
</pre>
<p>When requesting transient audio focus you have an additional option: whether or not you want to
enable "ducking." Normally, when a well-behaved audio app loses audio focus it immediately
silences its playback. By requesting a transient audio focus that allows ducking you tell other
audio apps that its acceptable for them to keep playing, provided they lower their volume until the
focus returns to them.</p>
<pre>
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// Start playback.
}
</pre>
<p>Ducking is particularly suitable for apps that use the audio stream intermittently, such as for
audible driving directions.</p>
<p>Whenever another app requests audio focus as described above, its choice between permanent and
transient (with or without support for ducking) audio focus is received by the listener you
registered when requesting focus.</p>
<h2 id="HandleFocusLoss">Handle the Loss of Audio Focus</h2>
<p>If your app can request audio focus, it follows that it will in turn lose that focus when another
app requests it. How your app responds to a loss of audio focus depends on the manner of that
loss.</p>
<p>The {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange
onAudioFocusChange()} callback method of they audio focus change listener you registered when
requesting audio focus receives a parameter that describes the focus change event. Specifically,
the possible focus loss events mirror the focus request types from the previous
section&mdash;permanent loss, transient loss, and transient with ducking permitted.</p>
<p>Generally speaking, a transient (temporary) loss of audio focus should result in your app
silencing its audio stream, but otherwise maintaining the same state. You should continue to
monitor changes in audio focus and be prepared to resume playback where it was paused once youve
regained the focus.</p>
<p>If the audio focus loss is permanent, its assumed that another application is now being used to
listen to audio and your app should effectively end itself. In practical terms, that means stopping
playback, removing media button listeners&mdash;allowing the new audio player to exclusively handle
those events&mdash;and abandoning your audio focus. At that point, you would expect a user action
(pressing play in your app) to be required before you resume playing audio.</p>
<p>In the following code snippet, we pause the playback or our media player object if the audio
loss is transien and resume it when we have regained the focus. If the loss is permanent, it
unregisters our media button event receiver and stops monitoring audio focus changes.<p>
<pre>
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
// Pause playback
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
am.abandonAudioFocus(afChangeListener);
// Stop playback
}
}
};
</pre>
<p>In the case of a transient loss of audio focus where ducking is permitted, rather than pausing
playback, you can "duck" instead.</p>
<h2 id="DUCK">Duck!</h2>
<p>Ducking is the process of lowering your audio stream output volume to make transient audio from
another app easier to hear without totally disrupting the audio from your own application.</p>
<p>In the following code snippet lowers the volume on our media player object when we temporarily
lose focus, then returns it to its previous level when we regain focus.</p>
<pre>
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
// Lower the volume
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Raise it back to normal
}
}
};
</pre>
<p>A loss of audio focus is the most important broadcast to react to, but not the only one. The
system broadcasts a number of intents to alert you to changes in users audio experience.
The next lesson demonstrates how to monitor them to improve the users overall experience.</p>

View File

@@ -0,0 +1,88 @@
page.title=Dealing with Audio Output Hardware
parent.title=Managing Audio Playback and Focus
parent.link=index.html
trainingnavtop=true
previous.title=Managing Audio Focus
previous.link=audio-focus.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#CheckHardware">Check What Hardware is Being Used</a></li>
<li><a href="#HandleChanges">Handle Changes in the Audio Output Hardware</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></li>
</ul>
</div>
</div>
<p>Users have a number of alternatives when it comes to enjoying the audio from their Android
devices. Most devices have a built-in speaker, headphone jacks for wired headsets, and many also
feature Bluetooth connectivity and support for A2DP audio. </p>
<h2 id="CheckHardware">Check What Hardware is Being Used</h2>
<p>How your app behaves might be affected by which hardware its output is being routed to.</p>
<p>You can query the {@link android.media.AudioManager} to determine if the audio is currently
being routed to the device speaker, wired headset, or attached Bluetooth device as shown in the
following snippet:</p>
<pre>
if (isBluetoothA2dpOn()) {
// Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
// Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
// Adjust output for headsets
} else {
// If audio plays and noone can hear it, is it still playing?
}
</pre>
<h2 id="HandleChanges">Handle Changes in the Audio Output Hardware</h2>
<p>When a headset is unplugged, or a Bluetooth device disconnected, the audio stream
automatically reroutes to the built in speaker. If you listen to your music at as high a volume as I
do, that can be a noisy surprise.</p>
<p>Luckily the system broadcasts an {@link android.media.AudioManager#ACTION_AUDIO_BECOMING_NOISY}
intent when this happens. Its good practice to register a {@link android.content.BroadcastReceiver}
that listens for this intent whenever youre playing audio. In the case of music players, users
typically expect the playback to be paused&mdash;while for games you may choose to significantly
lower the volume.</p>
<pre>
private class NoisyAudioStreamReceiver extends BroadcastReceiver {
&#64;Override
public void onReceive(Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
// Pause the playback
}
}
}
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private void startPlayback() {
registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}
private void stopPlayback() {
unregisterReceiver(myNoisyAudioStreamReceiver);
}
</pre>

View File

@@ -0,0 +1,62 @@
page.title=Managing Audio Playback and Focus
trainingnavtop=true
startpage=true
next.title=Controlling Your App's Volume and Playback
next.link=volume-playback.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and prerequisites</h2>
<ul>
<li>Android 2.0 (API level 5) or higher</li>
<li>Experience with <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media
Playback</a></li>
</ul>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a></li>
</ul>
</div>
</div>
<p>If your app plays audio, its important that your users can control the audio in a predictable
manner. To ensure a great user experience, its also important that your app manages the audio focus
to ensure multiple apps arent playing audio at the same time.</p>
<p>After this class, you will be able to build apps that respond to hardware audio key presses,
which request audio focus when playing audio, and which respond appropriately to changes in audio
focus caused by the system or other applications.</p>
<h2>Lessons</h2>
<!-- Create a list of the lessons in this class along with a short description of each lesson.
These should be short and to the point. It should be clear from reading the summary whether someone
will want to jump to a lesson or not.-->
<dl>
<dt><b><a href="volume-playback.html">Controlling Your Apps Volume and
Playback</a></b></dt>
<dd>Learn how to ensure your users can control the volume of your app using the hardware or
software volume controls and where available the play, stop, pause, skip, and previous media
playback keys.</dd>
<dt><b><a href="audio-focus.html">Managing Audio Focus</a></b></dt>
<dd>With multiple apps potentially playing audio it's important to think about how they should
interact. To avoid every music app playing at the same time, Android uses audio focus to moderate
audio playback. Learn how to request the audio focus, listen for a loss of audio focus, and how to
respond when that happens.</dd>
<dt><b><a href="audio-output.html">Dealing with Audio Output Hardware</a></b></dt>
<dd>Audio can be played from a number of sources. Learn how to find out where the audio is being
played and how to handle a headset being disconnected during playback.</dd>
</dl>

View File

@@ -0,0 +1,156 @@
page.title=Controlling Your Apps Volume and Playback
parent.title=Managing Audio Playback and Focus
parent.link=index.html
trainingnavtop=true
next.title=Managing Audio Focus
next.link=audio-focus.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#IdentifyStream">Identify Which Audio Stream to Use</a></li>
<li><a href="#HardwareVolumeKeys">Use Hardware Volume Keys to Control Your Apps Audio
Volume</a></li>
<li><a href="#PlaybackControls">Use Hardware Playback Control Keys to Control Your Apps Audio
Playback</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></li>
</ul>
</div>
</div>
<p>A good user experience is a predictable one. If your app plays media its important that your
users can control the volume of your app using the hardware or software volume controls of their
device, bluetooth headset, or headphones.</p>
<p>Similarly, where appropriate and available, the play, stop, pause, skip, and previous media
playback keys should perform their respective actions on the audio stream used by your app.</p>
<h2 id="IdentifyStream">Identify Which Audio Stream to Use</h2>
<p>The first step to creating a predictable audio experience is understanding which audio stream
your app will use.</p>
<p>Android maintains a separate audio stream for playing music, alarms, notifications, the incoming
call ringer, system sounds, in-call volume, and DTMF tones. This is done primarily to allow users to
control the volume of each stream independently.</p>
<p>Most of these streams are restricted to system events, so unless your app is a replacement alarm
clock, youll almost certainly be playing your audio using the {@link
android.media.AudioManager#STREAM_MUSIC} stream.</p>
<h2 id="HardwareVolumeKeys">Use Hardware Volume Keys to Control Your Apps Audio Volume</h2>
<p>By default, pressing the volume controls modify the volume of the active audio stream. If your
app isn't currently playing anything, hitting the volume keys adjusts the ringer volume.<p>
<p>If you've got a game or music app, then chances are good that when the user hits the volume keys
they want to control the volume of the game or music, even if theyre currently between songs or
theres no music in the current game location.</p>
<p>You may be tempted to try and listen for volume key presses and modify the volume of your
audio stream that way. Resist the urge. Android provides the handy {@link
android.app.Activity#setVolumeControlStream setVolumeControlStream()} method to direct volume key
presses to the audio stream you specify.<p>
<p>Having identified the audio stream your application
will be using, you should set it as the volume stream target. You should make this call early in
your apps lifecycle&mdash;because you only need to call it once during the activity lifecycle, you
should typically call it within the {@code onCreate()} method (of the {@link
android.app.Activity} or {@link android.app.Fragment} that controls
your media). This ensures that whenever your app is visible, the
volume controls function as the user expects.<p>
<pre>
setVolumeControlStream(AudioManager.STREAM_MUSIC);
</pre>
<p>From this point onwards, pressing the volume keys on the device affect the audio stream you
specify (in this case “music”) whenever the target activity or fragment is visible.</p>
<h2 id="PlaybackControls">Use Hardware Playback Control Keys to Control Your Apps Audio
Playback</h2>
<p>Media playback buttons such as play, pause, stop, skip, and previous are available on some
handsets and many connected or wireless headsets. Whenever a user presses one of these hardware
keys, the system broadcasts an intent with the {@link android.content.Intent#ACTION_MEDIA_BUTTON}
action.</p>
<p>To respond to media button clicks, you need to register a {@link
android.content.BroadcastReceiver} in your manifest that listens for this action broadcast as shown
below.</p>
<pre>
&lt;receiver android:name=".RemoteControlReceiver">
&lt;intent-filter>
&lt;action android:name="android.intent.action.MEDIA_BUTTON" />
&lt;/intent-filter>
&lt;/receiver>
</pre>
<p>The receiver implementation itself needs to extract which key was pressed to cause the broadcast.
The {@link android.content.Intent} includes this under the {@link
android.content.Intent#EXTRA_KEY_EVENT} key, while the {@link android.view.KeyEvent} class includes
a list {@code KEYCODE_MEDIA_*} static constants that represents each of the possible media
buttons, such as {@link android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} and {@link
android.view.KeyEvent#KEYCODE_MEDIA_NEXT}.</p>
<p>The following snippet shows how to extract the media button pressed and affects the media playback accordingly.</p>
<pre>
public class RemoteControlReceiver extends BroadcastReceiver {
&#64;Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
// Handle key press.
}
}
}
}
</pre>
<p>Because multiple applications might want to listen for media button presses, you must
also programmatically control when your app should receive media button press events.</p>
<p>The following code can be used within your app to register and de-register your media button
event receiver using the {@link android.media.AudioManager}. When registered, your broadcast
receiver is the exclusive receiver of all media button broadcasts.<p>
<pre>
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...
// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
</pre>
<p>Typically, apps should unregister most of their receivers whenever they become inactive or
invisible (such as during the {@link android.app.Activity#onStop onStop()} callback). However, its
not that simple for media playback apps&mdash;in fact, responding to media playback buttons is most
important when your application isnt visible and therefore cant be controlled by the on-screen
UI.</p>
<p>A better approach is to register and unregister the media button event receiver when your
application gains and losses the audio focus. This is covered in detail in the next lesson.</p>

View File

@@ -0,0 +1,156 @@
page.title=Monitoring the Battery Level and Charging State
parent.title=Monitoring Device State to Optimize Battery Life
parent.link=index.html
trainingnavtop=true
next.title=Determining and Monitoring the Docking State and Type
next.link=docking-monitoring.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#DetermineChargeState">Determine the Current Charging State</a></li>
<li><a href="#MonitorChargeState">Monitor Changes in Charging State</a></li>
<li><a href="#CurrentLevel">Determine the Current Battery Level</a></li>
<li><a href="#MonitorLevel">Monitor Significant Changes in Battery Level</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
</ul>
</div>
</div>
<p>When you're altering the frequency of your background updates to reduce the effect of those
updates on battery life, checking the current battery level and charging state is a good place to
start.</p>
<p>The battery-life impact of performing application updates depends on the battery level and
charging state of the device. The impact of performing updates while the device is charging over AC
is negligible, so in most cases you can maximize your refresh rate whenever the device is connected
to a wall charger. Conversely, if the device is discharging, reducing your update rate helps
prolong the battery life.</p>
<p>Similarly, you can check the battery charge level, potentially reducing the frequency of&mdash;or
even stopping&mdash;your updates when the battery charge is nearly exhausted.</p>
<h2 id="DetermineChargeState">Determine the Current Charging State</h2>
<p>Start by determining the current charge status. The {@link android.os.BatteryManager}
broadcasts all battery and charging details in a sticky {@link android.content.Intent} that includes
the charging status.</p>
<p>Because it's a sticky intent, you don't need to register a {@link
android.content.BroadcastReceiver}&mdash;by simply calling {@code registerReceiver} passing in
{@code null} as the receiver as shown in the next snippet, the current battery status intent is
returned. You could pass in an actual {@link android.content.BroadcastReceiver} object here, but
we'll be handling updates in a later section so it's not necessary.</p>
<pre>IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);</pre>
<p>You can extract both the current charging status and, if the device is being charged, whether
it's charging via USB or AC charger:<p>
<pre>// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;</pre>
<p>Typically you should maximize the rate of your background updates in the case where the device is
connected to an AC charger, reduce the rate if the charge is over USB, and lower it
further if the battery is discharging.</p>
<h2 id="MonitorChargeState">Monitor Changes in Charging State</h2>
<p>The charging status can change as easily as a device can be plugged in, so it's important to
monitor the charging state for changes and alter your refresh rate accordingly.</p>
<p>The {@link android.os.BatteryManager} broadcasts an action whenever the device is connected or
disconnected from power. It's important to to receive these events even while your app isn't
running&mdash;particularly as these events should impact how often you start your app in order to
initiate a background update&mdash;so you should register a {@link
android.content.BroadcastReceiver} in your manifest to listen for both events by defining the
{@link android.content.Intent#ACTION_POWER_CONNECTED} and {@link
android.content.Intent#ACTION_POWER_DISCONNECTED} within an intent filter.</p>
<pre>&lt;receiver android:name=".PowerConnectionReceiver">
&lt;intent-filter>
&lt;action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
&lt;action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
&lt;/intent-filter>
&lt;/receiver></pre>
<p>Within the associated {@link android.content.BroadcastReceiver} implementation, you can extract
the current charging state and method as described in the previous step.</p>
<pre>public class PowerConnectionReceiver extends BroadcastReceiver {
&#64;Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
}
}</pre>
<h2 id="CurrentLevel">Determine the Current Battery Level</h2>
<p>In some cases it's also useful to determine the current battery level. You may choose to reduce
the rate of your background updates if the battery charge is below a certain level.</p>
<p>You can find the current battery charge by extracting the current battery level and scale from
the battery status intent as shown here:</p>
<pre>int level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float)scale;</pre>
<h2 id="MonitorLevel">Monitor Significant Changes in Battery Level</h2>
<p>You can't easily continually monitor the battery state, but you don't need to.</p>
<p>Generally speaking, the impact of constantly monitoring the battery level has a greater
impact on the battery than your app's normal behavior, so it's good practice to only monitor
significant changes in battery level&mdash;specifically when the device enters or exits a low
battery state.</p>
<p>The manifest snippet below is extracted from the intent filter element within a broadcast
receiver. The receiver is triggered whenever the device battery becomes low or exits the low
condition by listening for {@link android.content.Intent#ACTION_BATTERY_LOW} and {@link
android.content.Intent#ACTION_BATTERY_OKAY}.</p>
<pre>&lt;receiver android:name=".BatteryLevelReceiver">
&lt;intent-filter>
&lt;action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
&lt;action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
&lt;/intent-filter>
&lt;/receiver></pre>
<p>It is generally good practice to disable all your background updates when the battery is
critically low. It doesn't matter how fresh your data is if the phone turns itself off before you
can make use of it.</p>
<p>In many cases, the act of charging a device is coincident with putting it into a dock. The next
lesson shows you how to determine the current dock state and monitor for changes in device
docking.</p>

View File

@@ -0,0 +1,90 @@
page.title=Determining and Monitoring the Connectivity Status
parent.title=Monitoring Device State to Optimize Battery Life
parent.link=index.html
trainingnavtop=true
previous.title=Determining and Monitoring the Docking State and Type
previous.link=docking-monitoring.html
next.title=Manipulating Broadcast Receivers On Demand
next.link=manifest-receivers.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#DetermineConnection">Determine if you Have an Internet Connection</a></li>
<li><a href="#DetermineType">Determine the Type of your Internet Connection</a></li>
<li><a href="#MonitorChanges">Monitor for Changes in Connectivity</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
</ul>
</div>
</div>
<p>Some of the most common uses for repeating alarms and background services is to schedule regular
updates of application data from Internet resources, cache data, or execute long running downloads.
But if you aren't connected to the Internet, or the connection is too slow to complete your
download, why both waking the device to schedule the update at all?</p>
<p>You can use the {@link android.net.ConnectivityManager} to check that you're actually
connected to the Internet, and if so, what type of connection is in place.</p>
<h2 id="DetermineConnection">Determine if You Have an Internet Connection</h2>
<p>There's no need to schedule an update based on an Internet resource if you aren't connected to
the Internet. The following snippet shows how to use the {@link android.net.ConnectivityManager}
to query the active network and determine if it has Internet connectivity.</p>
<pre>ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork.isConnectedOrConnecting();</pre>
<h2 id="DetermineType">Determine the Type of your Internet Connection</h2>
<p>It's also possible to determine the type of Internet connection currently available.</p>
<p>Device connectivity can be provided by mobile data, WiMAX, Wi-Fi, and ethernet connections. By
querying the type of the active network, as shown below, you can alter your refresh rate based on
the bandwidth available.</p>
<pre>boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;</pre>
<p>Mobile data costs tend to be significantly higher than Wi-Fi, so in most cases, your app's update
rate should be lower when on mobile connections. Similarly, downloads of significant size should be
suspended until you have a Wi-Fi connection.</p>
<p>Having disabled your updates, it's important that you listen for changes in connectivity in order
to resume them once an Internet connection has been established.</p>
<h2 id="MonitorChanges">Monitor for Changes in Connectivity</h2>
<p>The {@link android.net.ConnectivityManager} broadcasts the {@link
android.net.ConnectivityManager#CONNECTIVITY_ACTION} ({@code
"android.net.conn.CONNECTIVITY_CHANGE"}) action whenever the connectivity details have changed. You
can register a broadcast receiver in your manifest to listen for these changes and resume (or
suspend) your background updates accordingly.</p>
<pre>&lt;action android:name="android.net.conn.CONNECTIVITY_CHANGE"/></pre>
<p>Changes to a device's connectivity can be very frequent&mdash;this broadcast is triggered
every time you move between mobile data and Wi-Fi. As a result, it's good practice to monitor
this broadcast only when you've previously suspended updates or downloads in order to resume them.
It's generally sufficient to simply check for Internet connectivity before beginning an update and,
should there be none, suspend further updates until connectivity is restored.</p>
<p>This technique requires toggling broadcast receivers you've declard in the manifest, which is
described in the next lesson.</p>

View File

@@ -0,0 +1,90 @@
page.title=Determining and Monitoring the Docking State and Type
parent.title=Monitoring Device State to Optimize Battery Life
parent.link=index.html
trainingnavtop=true
previous.title= Monitoring the Battery Level and Charging State
previous.link=battery-monitoring.html
next.title= Determining and Monitoring the Connectivity Status
next.link=connectivity-monitoring.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#CurrentDockState">Request the Audio Focus</a></li>
<li><a href="#DockType">Determine the Current Dock Type</a></li>
<li><a href="#MonitorDockState">Monitor for Changes in the Dock State or Type</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
</ul>
</div>
</div>
<p>Android devices can be docked into several different kinds of docks. These include car or home
docks and digital versus analog docks. The dock-state is typically closely linked to the charging
state as many docks provide power to docked devices.</p>
<p>How the dock-state of the phone affects your update rate depends on your app. You may choose
to increase the update frequency of a sports center app when it's in the desktop dock, or disable
your updates completely if the device is car docked. Conversely, you may choose to maximize your
updates while car docked if your background service is updating traffic conditions.</p>
<p>The dock state is also broadcast as a sticky {@link android.content.Intent}, allowing you to
query if the device is docked or not, and if so, in which kind of dock.</p>
<h2 id="CurrentDockState">Determine the Current Docking State</h2>
<p>The dock-state details are included as an extra in a sticky broadcast of the {@link
android.content.Intent#ACTION_DOCK_EVENT} action. Because it's sticky, you don't need to register a
{@link android.content.BroadcastReceiver}. You can simply call {@link
android.content.Context#registerReceiver registerReceiver()} passing in {@code null} as the
broadcast receiver as shown in the next snippet.</p>
<pre>IntentFilter ifilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
Intent dockStatus = context.registerReceiver(null, ifilter);</pre>
<p>You can extract the current docking status from the {@code EXTRA_DOCK_STATE} extra:<p>
<pre>int dockState = battery.getIntExtra(EXTRA_DOCK_STATE, -1);
boolean isDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;</pre>
<h2 id="DockType">Determine the Current Dock Type</h2>
<p>If a device is docked, it can be docked in any one of four different type of dock:
<ul><li>Car</li>
<li>Desk</li>
<li>Low-End (Analog) Desk</li>
<li>High-End (Digital) Desk</li></ul></p>
<p>Note that the latter two options were only introduced to Android in API level 11, so it's good
practice to check for all three where you are only interested in the type of dock rather than it
being digital or analog specifically:</p>
<pre>boolean isCar = dockState == EXTRA_DOCK_STATE_CAR;
boolean isDesk = dockState == EXTRA_DOCK_STATE_DESK ||
dockState == EXTRA_DOCK_STATE_LE_DESK ||
dockState == EXTRA_DOCK_STATE_HE_DESK;</pre>
<h2 id="MonitorDockState">Monitor for Changes in the Dock State or Type</h2>
<p>Whenever the the device is docked or undocked, the {@link
android.content.Intent#ACTION_DOCK_EVENT} action is broadcast. To monitor changes in the
device's dock-state, simply register a broadcast receiver in your application manifest as shown in
the snippet below:</p>
<pre>&lt;action android:name="android.intent.action.ACTION_DOCK_EVENT"/></pre>
<p>You can extract the dock type and state within the receiver implementation using the same
techniques described in the previous step.</p>

View File

@@ -0,0 +1,63 @@
page.title=Monitoring Device State to Optimize Battery Life
trainingnavtop=true
startpage=true
next.title=Monitoring the Battery Level and Charging State
next.link=battery-monitoring.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and prerequisites</h2>
<ul>
<li>Android 2.0 (API level 5) or higher</li>
<li>Experience with <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a></li>
</ul>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a>
</ul>
</div>
</div>
<p>For your app to be a good citizen, it should seek to limit its impact on the battery life of its
host device. After this class you will be able to build apps that monitor modify their functionality
and behavior based on the state of the host device.</p>
<p>By taking steps such as disabling background service updates when you lose connectivity, or
reducing the rate of such updates when the battery level is low, you can ensure that the impact of
your app on battery life is minimized, without compromising the user experience.</p>
<h2>Lessons</h2>
<!-- Create a list of the lessons in this class along with a short description of each lesson.
These should be short and to the point. It should be clear from reading the summary whether someone
will want to jump to a lesson or not.-->
<dl>
<dt><b><a href="battery-monitoring.html">Monitoring the Battery Level and Charging State</a></b></dt>
<dd>Learn how to alter your app's update rate by determining, and monitoring, the current battery
level and changes in charging state.</dd>
<dt><b><a href="docking-monitoring.html">Determining and Monitoring the Docking State and
Type</a></b></dt>
<dd>Optimal refresh rates can vary based on how the host device is being used. Learn how to
determine, and monitor, the docking state and type of dock being used to affect your app's
behavior.</dd>
<dt><b><a href="connectivity-monitoring.html">Determining and Monitoring the Connectivity
Status</a></b></dt>
<dd>Without Internet connectivity you can't update your app from an online source. Learn how to
check the connectivity status to alter your background update rate. You'll also learn to check for
Wi-Fi or mobile connectivity before beginning high-bandwidth operations.</dd>
<dt><b><a href="manifest-receivers.html">Manipulating Broadcast Receivers On Demand</a></b></dt>
<dd>Broadcast receivers that you've declared in the manifest can be toggled at runtime to disable
those that aren't necessary due to the current device state. Learn to improve
efficiency by toggling and cascading state change receivers and delay actions until the device is in
a specific state.</dd>
</dl>

View File

@@ -0,0 +1,64 @@
page.title=Manipulating Broadcast Receivers On Demand
parent.title=Monitoring Device State to Optimize Battery Life
parent.link=index.html
trainingnavtop=true
previous.title=Determining and Monitoring the Connectivity Status
previous.link=connectivity-monitoring.html
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="ToggleReceivers">Toggle and Cascade State Change Receivers to Improve
Efficiency</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
</ul>
</div>
</div>
<p>The simplest way to monitor device state changes is to create a {@link
android.content.BroadcastReceiver} for each state you're monitoring and register each of them in
your application manifest. Then within each of these receivers you simply reschedule your recurring
alarms based on the current device state.</p>
<p>A side-effect of this approach is that your app will wake the device each time any of these
receivers is triggered&mdash;potentially much more frequently than required.</p>
<p>A better approach is to disable or enable the broadcast receivers at runtime. That way you can
use the receivers you declared in the manifest as passive alarms that are triggered by system events
only when necessary.</p>
<h2 id="ToggleReceivers">Toggle and Cascade State Change Receivers to Improve Efficiency </h2>
<p>Use can use the {@link android.content.pm.PackageManager} to toggle the enabled state on any
component defined in the manifest, including whichever broadcast receivers you wish to enable or
disable as shown in the snippet below:</p>
<pre>ComponentName receiver = new ComponentName(context, myReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP)</pre>
<p>Using this technique, if you determine that connectivity has been lost, you can disable all of
your receivers except the connectivity-change receiver. Conversely, once you are connected you can
stop listening for connectivity changes and simply check to see if you're online immediately before
performing an update and rescheduling a recurring update alarm.</p>
<p>You can use the same technique to delay a download that requires higher bandwidth to complete.
Simply enable a broadcast receiver that listens for connectivity changes and initiates the
download only after you are connected to Wi-Fi.</p>