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