Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 1 | page.title=Media Playback |
Scott Main | 64461bf | 2013-04-11 19:32:08 -0700 | [diff] [blame] | 2 | page.tags="mediaplayer","soundpool","audiomanager" |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 3 | @jd:body |
| 4 | |
| 5 | <div id="qv-wrapper"> |
| 6 | <div id="qv"> |
| 7 | |
| 8 | <h2>In this document</h2> |
| 9 | <ol> |
| 10 | <li><a href="#basics">The Basics</a> |
| 11 | <li><a href="#manifest">Manifest Declarations</a></li> |
| 12 | <li><a href="#mediaplayer">Using MediaPlayer</a> |
| 13 | <ol> |
| 14 | <li><a href='#preparingasync'>Asynchronous Preparation</a></li> |
| 15 | <li><a href='#managestate'>Managing State</a></li> |
| 16 | <li><a href='#releaseplayer'>Releasing the MediaPlayer</a></li> |
| 17 | </ol> |
| 18 | </li> |
| 19 | <li><a href="#mpandservices">Using a Service with MediaPlayer</a> |
| 20 | <ol> |
| 21 | <li><a href="#asyncprepare">Running asynchronously</a></li> |
| 22 | <li><a href="#asyncerror">Handling asynchronous errors</a></li> |
| 23 | <li><a href="#wakelocks">Using wake locks</a></li> |
| 24 | <li><a href="#foregroundserv">Running as a foreground service</a></li> |
| 25 | <li><a href="#audiofocus">Handling audio focus</a></li> |
| 26 | <li><a href="#cleanup">Performing cleanup</a></li> |
| 27 | </ol> |
| 28 | </li> |
| 29 | <li><a href="#noisyintent">Handling the AUDIO_BECOMING_NOISY Intent</a> |
| 30 | <li><a href="#viacontentresolver">Retrieving Media from a Content Resolver</a> |
| 31 | </ol> |
| 32 | |
| 33 | <h2>Key classes</h2> |
| 34 | <ol> |
| 35 | <li>{@link android.media.MediaPlayer}</li> |
| 36 | <li>{@link android.media.AudioManager}</li> |
| 37 | <li>{@link android.media.SoundPool}</li> |
| 38 | </ol> |
| 39 | |
| 40 | <h2>See also</h2> |
| 41 | <ol> |
| 42 | <li><a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a></li> |
| 43 | <li><a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a></li> |
| 44 | <li><a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media Formats</a></li> |
| 45 | <li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li> |
| 46 | </ol> |
| 47 | |
| 48 | </div> |
| 49 | </div> |
| 50 | |
| 51 | <p>The Android multimedia framework includes support for playing variety of common media types, so |
| 52 | that you can easily integrate audio, video and images into your applications. You can play audio or |
| 53 | video from media files stored in your application's resources (raw resources), from standalone files |
| 54 | in the filesystem, or from a data stream arriving over a network connection, all using {@link |
| 55 | android.media.MediaPlayer} APIs.</p> |
| 56 | |
| 57 | <p>This document shows you how to write a media-playing application that interacts with the user and |
| 58 | the system in order to obtain good performance and a pleasant user experience.</p> |
| 59 | |
| 60 | <p class="note"><strong>Note:</strong> You can play back the audio data only to the standard output |
| 61 | device. Currently, that is the mobile device speaker or a Bluetooth headset. You cannot play sound |
| 62 | files in the conversation audio during a call.</p> |
| 63 | |
| 64 | <h2 id="basics">The Basics</h2> |
| 65 | <p>The following classes are used to play sound and video in the Android framework:</p> |
| 66 | |
| 67 | <dl> |
| 68 | <dt>{@link android.media.MediaPlayer}</dt> |
| 69 | <dd>This class is the primary API for playing sound and video.</dd> |
| 70 | <dt>{@link android.media.AudioManager}</dt> |
| 71 | <dd>This class manages audio sources and audio output on a device.</dd> |
| 72 | </dl> |
| 73 | |
| 74 | <h2 id="manifest">Manifest Declarations</h2> |
| 75 | <p>Before starting development on your application using MediaPlayer, make sure your manifest has |
| 76 | the appropriate declarations to allow use of related features.</p> |
| 77 | |
| 78 | <ul> |
| 79 | <li><strong>Internet Permission</strong> - If you are using MediaPlayer to stream network-based |
| 80 | content, your application must request network access. |
| 81 | <pre> |
| 82 | <uses-permission android:name="android.permission.INTERNET" /> |
| 83 | </pre> |
| 84 | </li> |
| 85 | <li><strong>Wake Lock Permission</strong> - If your player application needs to keep the screen |
| 86 | from dimming or the processor from sleeping, or uses the {@link |
| 87 | android.media.MediaPlayer#setScreenOnWhilePlaying(boolean) MediaPlayer.setScreenOnWhilePlaying()} or |
| 88 | {@link android.media.MediaPlayer#setWakeMode(android.content.Context, int) |
| 89 | MediaPlayer.setWakeMode()} methods, you must request this permission. |
| 90 | <pre> |
| 91 | <uses-permission android:name="android.permission.WAKE_LOCK" /> |
| 92 | </pre> |
| 93 | </li> |
| 94 | </ul> |
| 95 | |
| 96 | <h2 id="mediaplayer">Using MediaPlayer</h2> |
| 97 | <p>One of the most important components of the media framework is the |
| 98 | {@link android.media.MediaPlayer MediaPlayer} |
| 99 | class. An object of this class can fetch, decode, and play both audio and video |
| 100 | with minimal setup. It supports several different media sources such as: |
| 101 | <ul> |
| 102 | <li>Local resources</li> |
| 103 | <li>Internal URIs, such as one you might obtain from a Content Resolver</li> |
| 104 | <li>External URLs (streaming)</li> |
| 105 | </ul> |
| 106 | </p> |
| 107 | |
| 108 | <p>For a list of media formats that Android supports, |
| 109 | see the <a href="{@docRoot}guide/appendix/media-formats.html">Android Supported Media |
| 110 | Formats</a> document. </p> |
| 111 | |
| 112 | <p>Here is an example |
| 113 | of how to play audio that's available as a local raw resource (saved in your application's |
| 114 | {@code res/raw/} directory):</p> |
| 115 | |
| 116 | <pre>MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); |
| 117 | mediaPlayer.start(); // no need to call prepare(); create() does that for you |
| 118 | </pre> |
| 119 | |
| 120 | <p>In this case, a "raw" resource is a file that the system does not |
| 121 | try to parse in any particular way. However, the content of this resource should not |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 122 | be raw audio. It should be a properly encoded and formatted media file in one |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 123 | of the supported formats.</p> |
| 124 | |
| 125 | <p>And here is how you might play from a URI available locally in the system |
| 126 | (that you obtained through a Content Resolver, for instance):</p> |
| 127 | |
| 128 | <pre>Uri myUri = ....; // initialize Uri here |
| 129 | MediaPlayer mediaPlayer = new MediaPlayer(); |
| 130 | mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); |
| 131 | mediaPlayer.setDataSource(getApplicationContext(), myUri); |
| 132 | mediaPlayer.prepare(); |
| 133 | mediaPlayer.start();</pre> |
| 134 | |
| 135 | <p>Playing from a remote URL via HTTP streaming looks like this:</p> |
| 136 | |
| 137 | <pre>String url = "http://........"; // your URL here |
| 138 | MediaPlayer mediaPlayer = new MediaPlayer(); |
| 139 | mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); |
| 140 | mediaPlayer.setDataSource(url); |
| 141 | mediaPlayer.prepare(); // might take long! (for buffering, etc) |
| 142 | mediaPlayer.start();</pre> |
| 143 | |
| 144 | <p class="note"><strong>Note:</strong> |
| 145 | If you're passing a URL to stream an online media file, the file must be capable of |
| 146 | progressive download.</p> |
| 147 | |
| 148 | <p class="caution"><strong>Caution:</strong> You must either catch or pass |
| 149 | {@link java.lang.IllegalArgumentException} and {@link java.io.IOException} when using |
| 150 | {@link android.media.MediaPlayer#setDataSource setDataSource()}, because |
| 151 | the file you are referencing might not exist.</p> |
| 152 | |
| 153 | <h3 id='preparingasync'>Asynchronous Preparation</h3> |
| 154 | |
| 155 | <p>Using {@link android.media.MediaPlayer MediaPlayer} can be straightforward in |
| 156 | principle. However, it's important to keep in mind that a few more things are |
| 157 | necessary to integrate it correctly with a typical Android application. For |
| 158 | example, the call to {@link android.media.MediaPlayer#prepare prepare()} can |
| 159 | take a long time to execute, because |
| 160 | it might involve fetching and decoding media data. So, as is the case with any |
| 161 | method that may take long to execute, you should <strong>never call it from your |
| 162 | application's UI thread</strong>. Doing that will cause the UI to hang until the method returns, |
| 163 | which is a very bad user experience and can cause an ANR (Application Not Responding) error. Even if |
| 164 | you expect your resource to load quickly, remember that anything that takes more than a tenth |
| 165 | of a second to respond in the UI will cause a noticeable pause and will give |
| 166 | the user the impression that your application is slow.</p> |
| 167 | |
| 168 | <p>To avoid hanging your UI thread, spawn another thread to |
| 169 | prepare the {@link android.media.MediaPlayer} and notify the main thread when done. However, while |
| 170 | you could write the threading logic |
| 171 | yourself, this pattern is so common when using {@link android.media.MediaPlayer} that the framework |
| 172 | supplies a convenient way to accomplish this task by using the |
| 173 | {@link android.media.MediaPlayer#prepareAsync prepareAsync()} method. This method |
| 174 | starts preparing the media in the background and returns immediately. When the media |
| 175 | is done preparing, the {@link android.media.MediaPlayer.OnPreparedListener#onPrepared onPrepared()} |
| 176 | method of the {@link android.media.MediaPlayer.OnPreparedListener |
| 177 | MediaPlayer.OnPreparedListener}, configured through |
| 178 | {@link android.media.MediaPlayer#setOnPreparedListener setOnPreparedListener()} is called.</p> |
| 179 | |
| 180 | <h3 id='managestate'>Managing State</h3> |
| 181 | |
| 182 | <p>Another aspect of a {@link android.media.MediaPlayer} that you should keep in mind is |
| 183 | that it's state-based. That is, the {@link android.media.MediaPlayer} has an internal state |
| 184 | that you must always be aware of when writing your code, because certain operations |
| 185 | are only valid when then player is in specific states. If you perform an operation while in the |
| 186 | wrong state, the system may throw an exception or cause other undesireable behaviors.</p> |
| 187 | |
| 188 | <p>The documentation in the |
| 189 | {@link android.media.MediaPlayer MediaPlayer} class shows a complete state diagram, |
| 190 | that clarifies which methods move the {@link android.media.MediaPlayer} from one state to another. |
| 191 | For example, when you create a new {@link android.media.MediaPlayer}, it is in the <em>Idle</em> |
| 192 | state. At that point, you should initialize it by calling |
| 193 | {@link android.media.MediaPlayer#setDataSource setDataSource()}, bringing it |
| 194 | to the <em>Initialized</em> state. After that, you have to prepare it using either the |
| 195 | {@link android.media.MediaPlayer#prepare prepare()} or |
| 196 | {@link android.media.MediaPlayer#prepareAsync prepareAsync()} method. When |
| 197 | the {@link android.media.MediaPlayer} is done preparing, it will then enter the <em>Prepared</em> |
| 198 | state, which means you can call {@link android.media.MediaPlayer#start start()} |
| 199 | to make it play the media. At that point, as the diagram illustrates, |
| 200 | you can move between the <em>Started</em>, <em>Paused</em> and <em>PlaybackCompleted</em> states by |
| 201 | calling such methods as |
| 202 | {@link android.media.MediaPlayer#start start()}, |
| 203 | {@link android.media.MediaPlayer#pause pause()}, and |
| 204 | {@link android.media.MediaPlayer#seekTo seekTo()}, |
| 205 | amongst others. When you |
| 206 | call {@link android.media.MediaPlayer#stop stop()}, however, notice that you |
| 207 | cannot call {@link android.media.MediaPlayer#start start()} again until you |
| 208 | prepare the {@link android.media.MediaPlayer} again.</p> |
| 209 | |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 210 | <p>Always keep <a href='{@docRoot}images/mediaplayer_state_diagram.gif'>the state diagram</a> |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 211 | in mind when writing code that interacts with a |
| 212 | {@link android.media.MediaPlayer} object, because calling its methods from the wrong state is a |
| 213 | common cause of bugs.</p> |
| 214 | |
| 215 | <h3 id='releaseplayer'>Releasing the MediaPlayer</h3> |
| 216 | |
| 217 | <p>A {@link android.media.MediaPlayer MediaPlayer} can consume valuable |
| 218 | system resources. |
| 219 | Therefore, you should always take extra precautions to make sure you are not |
| 220 | hanging on to a {@link android.media.MediaPlayer} instance longer than necessary. When you |
| 221 | are done with it, you should always call |
| 222 | {@link android.media.MediaPlayer#release release()} to make sure any |
| 223 | system resources allocated to it are properly released. For example, if you are |
| 224 | using a {@link android.media.MediaPlayer} and your activity receives a call to {@link |
| 225 | android.app.Activity#onStop onStop()}, you must release the {@link android.media.MediaPlayer}, |
| 226 | because it |
| 227 | makes little sense to hold on to it while your activity is not interacting with |
| 228 | the user (unless you are playing media in the background, which is discussed in the next section). |
| 229 | When your activity is resumed or restarted, of course, you need to |
| 230 | create a new {@link android.media.MediaPlayer} and prepare it again before resuming playback.</p> |
| 231 | |
| 232 | <p>Here's how you should release and then nullify your {@link android.media.MediaPlayer}:</p> |
| 233 | <pre> |
| 234 | mediaPlayer.release(); |
| 235 | mediaPlayer = null; |
| 236 | </pre> |
| 237 | |
| 238 | <p>As an example, consider the problems that could happen if you |
| 239 | forgot to release the {@link android.media.MediaPlayer} when your activity is stopped, but create a |
| 240 | new one when the activity starts again. As you may know, when the user changes the |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 241 | screen orientation (or changes the device configuration in another way), |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 242 | the system handles that by restarting the activity (by default), so you might quickly |
| 243 | consume all of the system resources as the user |
| 244 | rotates the device back and forth between portrait and landscape, because at each |
| 245 | orientation change, you create a new {@link android.media.MediaPlayer} that you never |
| 246 | release. (For more information about runtime restarts, see <a |
| 247 | href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a>.)</p> |
| 248 | |
| 249 | <p>You may be wondering what happens if you want to continue playing |
| 250 | "background media" even when the user leaves your activity, much in the same |
| 251 | way that the built-in Music application behaves. In this case, what you need is |
| 252 | a {@link android.media.MediaPlayer MediaPlayer} controlled by a {@link android.app.Service}, as |
Scott Main | cd1b08e | 2011-12-27 16:22:27 -0800 | [diff] [blame] | 253 | discussed in <a href="#mpandservices">Using a Service with MediaPlayer</a>.</p> |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 254 | |
| 255 | <h2 id="mpandservices">Using a Service with MediaPlayer</h2> |
| 256 | |
| 257 | <p>If you want your media to play in the background even when your application |
| 258 | is not onscreen—that is, you want it to continue playing while the user is |
| 259 | interacting with other applications—then you must start a |
| 260 | {@link android.app.Service Service} and control the |
| 261 | {@link android.media.MediaPlayer MediaPlayer} instance from there. |
| 262 | You should be careful about this setup, because the user and the system have expectations |
| 263 | about how an application running a background service should interact with the rest of the |
| 264 | system. If your application does not fulfil those expectations, the user may |
| 265 | have a bad experience. This section describes the main issues that you should be |
| 266 | aware of and offers suggestions about how to approach them.</p> |
| 267 | |
| 268 | |
| 269 | <h3 id="asyncprepare">Running asynchronously</h3> |
| 270 | |
| 271 | <p>First of all, like an {@link android.app.Activity Activity}, all work in a |
| 272 | {@link android.app.Service Service} is done in a single thread by |
| 273 | default—in fact, if you're running an activity and a service from the same application, they |
| 274 | use the same thread (the "main thread") by default. Therefore, services need to |
| 275 | process incoming intents quickly |
| 276 | and never perform lengthy computations when responding to them. If any heavy |
| 277 | work or blocking calls are expected, you must do those tasks asynchronously: either from |
| 278 | another thread you implement yourself, or using the framework's many facilities |
| 279 | for asynchronous processing.</p> |
| 280 | |
| 281 | <p>For instance, when using a {@link android.media.MediaPlayer} from your main thread, |
| 282 | you should call {@link android.media.MediaPlayer#prepareAsync prepareAsync()} rather than |
| 283 | {@link android.media.MediaPlayer#prepare prepare()}, and implement |
| 284 | a {@link android.media.MediaPlayer.OnPreparedListener MediaPlayer.OnPreparedListener} |
| 285 | in order to be notified when the preparation is complete and you can start playing. |
| 286 | For example:</p> |
| 287 | |
| 288 | <pre> |
| 289 | public class MyService extends Service implements MediaPlayer.OnPreparedListener { |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 290 | private static final String ACTION_PLAY = "com.example.action.PLAY"; |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 291 | MediaPlayer mMediaPlayer = null; |
| 292 | |
| 293 | public int onStartCommand(Intent intent, int flags, int startId) { |
| 294 | ... |
| 295 | if (intent.getAction().equals(ACTION_PLAY)) { |
| 296 | mMediaPlayer = ... // initialize it here |
| 297 | mMediaPlayer.setOnPreparedListener(this); |
| 298 | mMediaPlayer.prepareAsync(); // prepare async to not block main thread |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | /** Called when MediaPlayer is ready */ |
| 303 | public void onPrepared(MediaPlayer player) { |
| 304 | Â Â player.start(); |
| 305 | } |
| 306 | } |
| 307 | </pre> |
| 308 | |
| 309 | |
| 310 | <h3 id="asyncerror">Handling asynchronous errors</h3> |
| 311 | |
| 312 | <p>On synchronous operations, errors would normally |
| 313 | be signaled with an exception or an error code, but whenever you use asynchronous |
| 314 | resources, you should make sure your application is notified |
| 315 | of errors appropriately. In the case of a {@link android.media.MediaPlayer MediaPlayer}, |
| 316 | you can accomplish this by implementing a |
| 317 | {@link android.media.MediaPlayer.OnErrorListener MediaPlayer.OnErrorListener} and |
| 318 | setting it in your {@link android.media.MediaPlayer} instance:</p> |
| 319 | |
| 320 | <pre> |
| 321 | public class MyService extends Service implements MediaPlayer.OnErrorListener { |
| 322 | MediaPlayer mMediaPlayer; |
| 323 | |
| 324 | public void initMediaPlayer() { |
| 325 | // ...initialize the MediaPlayer here... |
| 326 | |
| 327 | mMediaPlayer.setOnErrorListener(this); |
| 328 | } |
| 329 | |
| 330 | @Override |
| 331 | public boolean onError(MediaPlayer mp, int what, int extra) { |
| 332 | // ... react appropriately ... |
| 333 | // The MediaPlayer has moved to the Error state, must be reset! |
| 334 | } |
| 335 | } |
| 336 | </pre> |
| 337 | |
| 338 | <p>It's important to remember that when an error occurs, the {@link android.media.MediaPlayer} |
| 339 | moves to the <em>Error</em> state (see the documentation for the |
| 340 | {@link android.media.MediaPlayer MediaPlayer} class for the full state diagram) |
| 341 | and you must reset it before you can use it again. |
| 342 | |
| 343 | |
| 344 | <h3 id="wakelocks">Using wake locks</h3> |
| 345 | |
| 346 | <p>When designing applications that play media |
| 347 | in the background, the device may go to sleep |
| 348 | while your service is running. Because the Android system tries to conserve |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 349 | battery while the device is sleeping, the system tries to shut off any |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 350 | of the phone's features that are |
| 351 | not necessary, including the CPU and the WiFi hardware. |
| 352 | However, if your service is playing or streaming music, you want to prevent |
| 353 | the system from interfering with your playback.</p> |
| 354 | |
| 355 | <p>In order to ensure that your service continues to run under |
| 356 | those conditions, you have to use "wake locks." A wake lock is a way to signal to |
| 357 | the system that your application is using some feature that should |
| 358 | stay available even if the phone is idle.</p> |
| 359 | |
| 360 | <p class="caution"><strong>Notice:</strong> You should always use wake locks sparingly and hold them |
| 361 | only for as long as truly necessary, because they significantly reduce the battery life of the |
| 362 | device.</p> |
| 363 | |
| 364 | <p>To ensure that the CPU continues running while your {@link android.media.MediaPlayer} is |
| 365 | playing, call the {@link android.media.MediaPlayer#setWakeMode |
| 366 | setWakeMode()} method when initializing your {@link android.media.MediaPlayer}. Once you do, |
| 367 | the {@link android.media.MediaPlayer} holds the specified lock while playing and releases the lock |
| 368 | when paused or stopped:</p> |
| 369 | |
| 370 | <pre> |
| 371 | mMediaPlayer = new MediaPlayer(); |
| 372 | // ... other initialization here ... |
| 373 | mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); |
| 374 | </pre> |
| 375 | |
| 376 | <p>However, the wake lock acquired in this example guarantees only that the CPU remains awake. If |
| 377 | you are streaming media over the |
| 378 | network and you are using Wi-Fi, you probably want to hold a |
| 379 | {@link android.net.wifi.WifiManager.WifiLock WifiLock} as |
| 380 | well, which you must acquire and release manually. So, when you start preparing the |
| 381 | {@link android.media.MediaPlayer} with the remote URL, you should create and acquire the Wi-Fi lock. |
| 382 | For example:</p> |
| 383 | |
| 384 | <pre> |
| 385 | WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) |
| 386 | .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); |
| 387 | |
| 388 | wifiLock.acquire(); |
| 389 | </pre> |
| 390 | |
| 391 | <p>When you pause or stop your media, or when you no longer need the |
| 392 | network, you should release the lock:</p> |
| 393 | |
| 394 | <pre> |
| 395 | wifiLock.release(); |
| 396 | </pre> |
| 397 | |
| 398 | |
| 399 | <h3 id="foregroundserv">Running as a foreground service</h3> |
| 400 | |
| 401 | <p>Services are often used for performing background tasks, such as fetching emails, |
| 402 | synchronizing data, downloading content, amongst other possibilities. In these |
| 403 | cases, the user is not actively aware of the service's execution, and probably |
| 404 | wouldn't even notice if some of these services were interrupted and later restarted.</p> |
| 405 | |
| 406 | <p>But consider the case of a service that is playing music. Clearly this is a service that the user |
| 407 | is actively aware of and the experience would be severely affected by any interruptions. |
| 408 | Additionally, it's a service that the user will likely wish to interact with during its execution. |
| 409 | In this case, the service should run as a "foreground service." A |
| 410 | foreground service holds a higher level of importance within the system—the system will |
| 411 | almost never kill the service, because it is of immediate importance to the user. When running |
| 412 | in the foreground, the service also must provide a status bar notification to ensure that users are |
| 413 | aware of the running service and allow them to open an activity that can interact with the |
| 414 | service.</p> |
| 415 | |
| 416 | <p>In order to turn your service into a foreground service, you must create a |
| 417 | {@link android.app.Notification Notification} for the status bar and call |
| 418 | {@link android.app.Service#startForeground startForeground()} from the {@link |
| 419 | android.app.Service}. For example:</p> |
| 420 | |
| 421 | <pre>String songName; |
| 422 | // assign the song name to songName |
| 423 | PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, |
| 424 | Â Â Â Â Â Â Â Â new Intent(getApplicationContext(), MainActivity.class), |
| 425 | Â Â Â Â Â Â Â Â PendingIntent.FLAG_UPDATE_CURRENT); |
| 426 | Notification notification = new Notification(); |
| 427 | notification.tickerText = text; |
| 428 | notification.icon = R.drawable.play0; |
| 429 | notification.flags |= Notification.FLAG_ONGOING_EVENT; |
| 430 | notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample", |
| 431 | Â Â Â Â Â Â Â Â "Playing: " + songName, pi); |
| 432 | startForeground(NOTIFICATION_ID, notification); |
| 433 | </pre> |
| 434 | |
| 435 | <p>While your service is running in the foreground, the notification you |
| 436 | configured is visible in the notification area of the device. If the user |
| 437 | selects the notification, the system invokes the {@link android.app.PendingIntent} you supplied. In |
| 438 | the example above, it opens an activity ({@code MainActivity}).</p> |
| 439 | |
| 440 | <p>Figure 1 shows how your notification appears to the user:</p> |
| 441 | |
| 442 | <img src='images/notification1.png' /> |
| 443 | |
| 444 | <img src='images/notification2.png' /> |
| 445 | <p class="img-caption"><strong>Figure 1.</strong> Screenshots of a foreground service's |
| 446 | notification, showing the notification icon in the status bar (left) and the expanded view |
| 447 | (right).</p> |
| 448 | |
| 449 | <p>You should only hold on to the "foreground service" status while your |
| 450 | service is actually performing something the user is actively aware of. Once |
| 451 | that is no longer true, you should release it by calling |
| 452 | {@link android.app.Service#stopForeground stopForeground()}:</p> |
| 453 | |
| 454 | <pre> |
| 455 | stopForeground(true); |
| 456 | </pre> |
| 457 | |
| 458 | <p>For more information, see the documentation about <a |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 459 | href="{@docRoot}guide/components/services.html#Foreground">Services</a> and |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 460 | <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>.</p> |
| 461 | |
| 462 | |
| 463 | <h3 id="audiofocus">Handling audio focus</h3> |
| 464 | |
| 465 | <p>Even though only one activity can run at any given time, Android is a |
| 466 | multi-tasking environment. This poses a particular challenge to applications |
| 467 | that use audio, because there is only one audio output and there may be several |
| 468 | media services competing for its use. Before Android 2.2, there was no built-in |
| 469 | mechanism to address this issue, which could in some cases lead to a bad user |
| 470 | experience. For example, when a user is listening to |
| 471 | music and another application needs to notify the user of something very important, |
| 472 | the user might not hear the notification tone due to the loud music. Starting with |
| 473 | Android 2.2, the platform offers a way for applications to negotiate their |
| 474 | use of the device's audio output. This mechanism is called Audio Focus.</p> |
| 475 | |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 476 | <p>When your application needs to output audio such as music or a notification, |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 477 | you should always request audio focus. Once it has focus, it can use the sound output freely, but it |
| 478 | should |
| 479 | always listen for focus changes. If it is notified that it has lost the audio |
| 480 | focus, it should immediately either kill the audio or lower it to a quiet level |
| 481 | (known as "ducking"—there is a flag that indicates which one is appropriate) and only resume |
| 482 | loud playback after it receives focus again.</p> |
| 483 | |
| 484 | <p>Audio Focus is cooperative in nature. That is, applications are expected |
| 485 | (and highly encouraged) to comply with the audio focus guidelines, but the |
| 486 | rules are not enforced by the system. If an application wants to play loud |
| 487 | music even after losing audio focus, nothing in the system will prevent that. |
| 488 | However, the user is more likely to have a bad experience and will be more |
| 489 | likely to uninstall the misbehaving application.</p> |
| 490 | |
| 491 | <p>To request audio focus, you must call |
| 492 | {@link android.media.AudioManager#requestAudioFocus requestAudioFocus()} from the {@link |
| 493 | android.media.AudioManager}, as the example below demonstrates:</p> |
| 494 | |
| 495 | <pre> |
| 496 | AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
| 497 | int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, |
| 498 | AudioManager.AUDIOFOCUS_GAIN); |
| 499 | |
| 500 | if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { |
| 501 | // could not get audio focus. |
| 502 | } |
| 503 | </pre> |
| 504 | |
| 505 | <p>The first parameter to {@link android.media.AudioManager#requestAudioFocus requestAudioFocus()} |
| 506 | is an {@link android.media.AudioManager.OnAudioFocusChangeListener |
| 507 | AudioManager.OnAudioFocusChangeListener}, |
| 508 | whose {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange |
| 509 | onAudioFocusChange()} method is called whenever there is a change in audio focus. Therefore, you |
| 510 | should also implement this interface on your service and activities. For example:</p> |
| 511 | |
| 512 | <pre> |
| 513 | class MyService extends Service |
| 514 | implements AudioManager.OnAudioFocusChangeListener { |
| 515 | // .... |
| 516 | public void onAudioFocusChange(int focusChange) { |
| 517 | // Do something based on focus change... |
| 518 | } |
| 519 | } |
| 520 | </pre> |
| 521 | |
| 522 | <p>The <code>focusChange</code> parameter tells you how the audio focus has changed, and |
| 523 | can be one of the following values (they are all constants defined in |
| 524 | {@link android.media.AudioManager AudioManager}):</p> |
| 525 | |
| 526 | <ul> |
| 527 | <li>{@link android.media.AudioManager#AUDIOFOCUS_GAIN}: You have gained the audio focus.</li> |
| 528 | |
| 529 | <li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS}: You have lost the audio focus for a |
| 530 | presumably long time. |
| 531 | You must stop all audio playback. Because you should expect not to have focus back |
| 532 | for a long time, this would be a good place to clean up your resources as much |
| 533 | as possible. For example, you should release the {@link android.media.MediaPlayer}.</li> |
| 534 | |
| 535 | <li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}: You have |
| 536 | temporarily lost audio focus, but should receive it back shortly. You must stop |
| 537 | all audio playback, but you can keep your resources because you will probably get |
| 538 | focus back shortly.</li> |
| 539 | |
| 540 | <li>{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}: You have temporarily |
| 541 | lost audio focus, |
| 542 | but you are allowed to continue to play audio quietly (at a low volume) instead |
| 543 | of killing audio completely.</li> |
| 544 | </ul> |
| 545 | |
| 546 | <p>Here is an example implementation:</p> |
| 547 | |
| 548 | <pre> |
| 549 | public void onAudioFocusChange(int focusChange) { |
| 550 | switch (focusChange) { |
| 551 | case AudioManager.AUDIOFOCUS_GAIN: |
| 552 | // resume playback |
| 553 | if (mMediaPlayer == null) initMediaPlayer(); |
| 554 | else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start(); |
| 555 | mMediaPlayer.setVolume(1.0f, 1.0f); |
| 556 | break; |
| 557 | |
| 558 | case AudioManager.AUDIOFOCUS_LOSS: |
| 559 | // Lost focus for an unbounded amount of time: stop playback and release media player |
| 560 | if (mMediaPlayer.isPlaying()) mMediaPlayer.stop(); |
| 561 | mMediaPlayer.release(); |
| 562 | mMediaPlayer = null; |
| 563 | break; |
| 564 | |
| 565 | case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: |
| 566 | // Lost focus for a short time, but we have to stop |
| 567 | // playback. We don't release the media player because playback |
| 568 | // is likely to resume |
| 569 | if (mMediaPlayer.isPlaying()) mMediaPlayer.pause(); |
| 570 | break; |
| 571 | |
| 572 | case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: |
| 573 | // Lost focus for a short time, but it's ok to keep playing |
| 574 | // at an attenuated level |
| 575 | if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f); |
| 576 | break; |
| 577 | } |
| 578 | } |
| 579 | </pre> |
| 580 | |
| 581 | <p>Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2) |
| 582 | and above, so if you want to support previous |
| 583 | versions of Android, you should adopt a backward compatibility strategy that |
| 584 | allows you to use this feature if available, and fall back seamlessly if not.</p> |
| 585 | |
| 586 | <p>You can achieve backward compatibility either by calling the audio focus methods by reflection |
| 587 | or by implementing all the audio focus features in a separate class (say, |
| 588 | <code>AudioFocusHelper</code>). Here is an example of such a class:</p> |
| 589 | |
| 590 | <pre> |
| 591 | public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { |
| 592 | AudioManager mAudioManager; |
| 593 | |
| 594 | // other fields here, you'll probably hold a reference to an interface |
| 595 | // that you can use to communicate the focus changes to your Service |
| 596 | |
| 597 | public AudioFocusHelper(Context ctx, /* other arguments here */) { |
| 598 | mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); |
| 599 | // ... |
| 600 | } |
| 601 | |
| 602 | public boolean requestFocus() { |
| 603 | return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == |
| 604 | mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC, |
| 605 | AudioManager.AUDIOFOCUS_GAIN); |
| 606 | } |
| 607 | |
| 608 | public boolean abandonFocus() { |
| 609 | return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == |
| 610 | mAudioManager.abandonAudioFocus(this); |
| 611 | } |
| 612 | |
| 613 | @Override |
| 614 | public void onAudioFocusChange(int focusChange) { |
| 615 | // let your service know about the focus change |
| 616 | } |
| 617 | } |
| 618 | </pre> |
| 619 | |
| 620 | |
| 621 | <p>You can create an instance of <code>AudioFocusHelper</code> class only if you detect that |
| 622 | the system is running API level 8 or above. For example:</p> |
| 623 | |
| 624 | <pre> |
| 625 | if (android.os.Build.VERSION.SDK_INT >= 8) { |
| 626 | mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this); |
| 627 | } else { |
| 628 | mAudioFocusHelper = null; |
| 629 | } |
| 630 | </pre> |
| 631 | |
| 632 | |
| 633 | <h3 id="cleanup">Performing cleanup</h3> |
| 634 | |
| 635 | <p>As mentioned earlier, a {@link android.media.MediaPlayer} object can consume a significant |
| 636 | amount of system resources, so you should keep it only for as long as you need and call |
| 637 | {@link android.media.MediaPlayer#release release()} when you are done with it. It's important |
| 638 | to call this cleanup method explicitly rather than rely on system garbage collection because |
| 639 | it might take some time before the garbage collector reclaims the {@link android.media.MediaPlayer}, |
| 640 | as it's only sensitive to memory needs and not to shortage of other media-related resources. |
| 641 | So, in the case when you're using a service, you should always override the |
| 642 | {@link android.app.Service#onDestroy onDestroy()} method to make sure you are releasing |
| 643 | the {@link android.media.MediaPlayer}:</p> |
| 644 | |
| 645 | <pre> |
| 646 | public class MyService extends Service { |
| 647 | MediaPlayer mMediaPlayer; |
| 648 | // ... |
| 649 | |
| 650 | @Override |
| 651 | public void onDestroy() { |
| 652 | if (mMediaPlayer != null) mMediaPlayer.release(); |
| 653 | } |
| 654 | } |
| 655 | </pre> |
| 656 | |
| 657 | <p>You should always look for other opportunities to release your {@link android.media.MediaPlayer} |
| 658 | as well, apart from releasing it when being shut down. For example, if you expect not |
| 659 | to be able to play media for an extended period of time (after losing audio focus, for example), |
| 660 | you should definitely release your existing {@link android.media.MediaPlayer} and create it again |
| 661 | later. On the |
| 662 | other hand, if you only expect to stop playback for a very short time, you should probably |
| 663 | hold on to your {@link android.media.MediaPlayer} to avoid the overhead of creating and preparing it |
| 664 | again.</p> |
| 665 | |
| 666 | |
| 667 | |
| 668 | <h2 id="noisyintent">Handling the AUDIO_BECOMING_NOISY Intent</h2> |
| 669 | |
| 670 | <p>Many well-written applications that play audio automatically stop playback when an event |
| 671 | occurs that causes the audio to become noisy (ouput through external speakers). For instance, |
| 672 | this might happen when a user is listening to music through headphones and accidentally |
| 673 | disconnects the headphones from the device. However, this behavior does not happen automatically. |
| 674 | If you don't implement this feature, audio plays out of the device's external speakers, which |
| 675 | might not be what the user wants.</p> |
| 676 | |
| 677 | <p>You can ensure your app stops playing music in these situations by handling |
| 678 | the {@link android.media.AudioManager#ACTION_AUDIO_BECOMING_NOISY} intent, for which you can |
| 679 | register a receiver by |
| 680 | adding the following to your manifest:</p> |
| 681 | |
| 682 | <pre> |
| 683 | <receiver android:name=".MusicIntentReceiver"> |
| 684 | <intent-filter> |
| 685 | <action android:name="android.media.AUDIO_BECOMING_NOISY" /> |
| 686 | </intent-filter> |
| 687 | </receiver> |
| 688 | </pre> |
| 689 | |
| 690 | <p>This registers the <code>MusicIntentReceiver</code> class as a broadcast receiver for that |
| 691 | intent. You should then implement this class:</p> |
| 692 | |
| 693 | <pre> |
Joe Fernandez | 6bfef51 | 2014-01-06 15:16:36 -0800 | [diff] [blame] | 694 | public class MusicIntentReceiver extends android.content.BroadcastReceiver { |
Joe Fernandez | 6d78639 | 2011-08-22 15:49:52 -0700 | [diff] [blame] | 695 | @Override |
| 696 | public void onReceive(Context ctx, Intent intent) { |
| 697 | if (intent.getAction().equals( |
| 698 | android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { |
| 699 | // signal your service to stop playback |
| 700 | // (via an Intent, for instance) |
| 701 | } |
| 702 | } |
| 703 | } |
| 704 | </pre> |
| 705 | |
| 706 | |
| 707 | |
| 708 | |
| 709 | <h2 id="viacontentresolver">Retrieving Media from a Content Resolver</h2> |
| 710 | |
| 711 | <p>Another feature that may be useful in a media player application is the ability to |
| 712 | retrieve music that the user has on the device. You can do that by querying the {@link |
| 713 | android.content.ContentResolver} for external media:</p> |
| 714 | |
| 715 | <pre> |
| 716 | ContentResolver contentResolver = getContentResolver(); |
| 717 | Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; |
| 718 | Cursor cursor = contentResolver.query(uri, null, null, null, null); |
| 719 | if (cursor == null) { |
| 720 | // query failed, handle error. |
| 721 | } else if (!cursor.moveToFirst()) { |
| 722 | // no media on the device |
| 723 | } else { |
| 724 | int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); |
| 725 | int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); |
| 726 | do { |
| 727 | long thisId = cursor.getLong(idColumn); |
| 728 | String thisTitle = cursor.getString(titleColumn); |
| 729 | // ...process entry... |
| 730 | } while (cursor.moveToNext()); |
| 731 | } |
| 732 | </pre> |
| 733 | |
| 734 | <p>To use this with the {@link android.media.MediaPlayer}, you can do this:</p> |
| 735 | |
| 736 | <pre> |
| 737 | long id = /* retrieve it from somewhere */; |
| 738 | Uri contentUri = ContentUris.withAppendedId( |
| 739 | android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); |
| 740 | |
| 741 | mMediaPlayer = new MediaPlayer(); |
| 742 | mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); |
| 743 | mMediaPlayer.setDataSource(getApplicationContext(), contentUri); |
| 744 | |
| 745 | // ...prepare and start... |
| 746 | </pre> |