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:
183
docs/html/training/managing-audio/audio-focus.jd
Normal file
183
docs/html/training/managing-audio/audio-focus.jd
Normal 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—only apps that hold the audio focus should play audio.</p>
|
||||
|
||||
<p>Before your app starts playing audio it should request—and receive—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 it’s 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—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 it’s 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 you’ve
|
||||
regained the focus.</p>
|
||||
|
||||
<p>If the audio focus loss is permanent, it’s 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—allowing the new audio player to exclusively handle
|
||||
those events—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 user’s audio experience.
|
||||
The next lesson demonstrates how to monitor them to improve the user’s overall experience.</p>
|
||||
88
docs/html/training/managing-audio/audio-output.jd
Normal file
88
docs/html/training/managing-audio/audio-output.jd
Normal 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. It’s good practice to register a {@link android.content.BroadcastReceiver}
|
||||
that listens for this intent whenever you’re playing audio. In the case of music players, users
|
||||
typically expect the playback to be paused—while for games you may choose to significantly
|
||||
lower the volume.</p>
|
||||
|
||||
<pre>
|
||||
private class NoisyAudioStreamReceiver extends BroadcastReceiver {
|
||||
@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>
|
||||
62
docs/html/training/managing-audio/index.jd
Normal file
62
docs/html/training/managing-audio/index.jd
Normal 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, it’s important that your users can control the audio in a predictable
|
||||
manner. To ensure a great user experience, it’s also important that your app manages the audio focus
|
||||
to ensure multiple apps aren’t 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 App’s 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>
|
||||
156
docs/html/training/managing-audio/volume-playback.jd
Normal file
156
docs/html/training/managing-audio/volume-playback.jd
Normal file
@@ -0,0 +1,156 @@
|
||||
page.title=Controlling Your App’s 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 App’s Audio
|
||||
Volume</a></li>
|
||||
<li><a href="#PlaybackControls">Use Hardware Playback Control Keys to Control Your App’s 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 it’s 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, you’ll 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 App’s 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 they’re currently between songs or
|
||||
there’s 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 app’s lifecycle—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 App’s 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>
|
||||
<receiver android:name=".RemoteControlReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</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 {
|
||||
@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, it’s
|
||||
not that simple for media playback apps—in fact, responding to media playback buttons is most
|
||||
important when your application isn’t visible and therefore can’t 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>
|
||||
156
docs/html/training/monitoring-device-state/battery-monitoring.jd
Normal file
156
docs/html/training/monitoring-device-state/battery-monitoring.jd
Normal 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—or
|
||||
even stopping—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}—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—particularly as these events should impact how often you start your app in order to
|
||||
initiate a background update—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><receiver android:name=".PowerConnectionReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||
</intent-filter>
|
||||
</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 {
|
||||
@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—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><receiver android:name=".BatteryLevelReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
|
||||
<action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
|
||||
</intent-filter>
|
||||
</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>
|
||||
|
||||
@@ -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><action android:name="android.net.conn.CONNECTIVITY_CHANGE"/></pre>
|
||||
|
||||
<p>Changes to a device's connectivity can be very frequent—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>
|
||||
@@ -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><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>
|
||||
63
docs/html/training/monitoring-device-state/index.jd
Normal file
63
docs/html/training/monitoring-device-state/index.jd
Normal 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>
|
||||
@@ -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—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>
|
||||
Reference in New Issue
Block a user