【Android Dev Guide - 04】 - Media - 学习使用MediaPlayer播放音乐

来源:互联网 发布:java开发简历自我评价 编辑:程序博客网 时间:2024/05/14 18:14
原文来自:http://blog.csdn.net/kesenhoo/article/details/6682109

目录(?)[-]

  1. 1Using MediaPlayer
    1. 11Asynchronous Preparation
    2. 12Managing State
    3. 13Releasing the MediaPlayer
  2. 2Using a Service with MediaPlayer
    1. 21Running asynchronously
    2. 22Handling asynchronous errors
    3. 23Using wake locks
    4. 24Running as a foreground service
    5. 25Handling audio focus
    6. 26Performing cleanup
  3. 3Handling the AUDIO_BECOMING_NOISY Intent
  4. 4Retrieving Media from a Content Resolver
  5. 5Playing JET content
  6. 6Performing Audio Capture

内容文字太多,根据自己的理解做了一些简略的陈述,如果能自己对照看看官方英文原文,效果会好很多,翻译的不好,有很多不当的地方还望指正,谢谢!后面有时间将写一篇文章详细解释如何实现一个完整的音乐播放器


【0】The Android multimedia framework includes support for encoding and decoding a variety of common media types, so that you can easily integrate audio, video and images into your applications. You can play audio or video from media files stored in your application's resources (raw resources), from standalone files in the filesystem, or from a data stream arriving over a network connection, all using MediaPlayer APIs.

Android多媒体framework包含了一系列的常见多媒体格式的编码与解码支持。因此你可以简单的把音频,视频,和图片插入到你的AP当中去。你可以对从应用的资源文件下,从文件系统中的独立文件或者从网络上的流媒体的方式的多媒体文件进行播放视频与音频,这些都只需要使用mediaPlayer的API即可。


You can also record audio and video using the MediaRecorderAPIs if supported by the device hardware. Note that the emulator doesn't have hardware to capture audio or video, but actual mobile devices are likely to provide these capabilities.

如果你的设备支持的话,你也可以通过使用mediaRecorder的API进行录制视频与音频。注意模拟器没有硬件设备支持获取音频与视频,实际的手机设备是可以的。


This document shows you how to write a media-playing application that interacts with the user and the system in order to obtain good performance and a pleasant user experience.

这份文档将想你展示如何使用mediaPlayer播放多媒体文件,并且如何获得一个良好的运行效果与愉悦的用户体验。


Note: You can play back the audio data only to the standard output device. Currently, that is the mobile device speaker or a Bluetooth headset. You cannot play sound files in the conversation audio during a call.

注意:你可以用标准输出设备进行音频的回放,目前,输出设备是手机扩音器与蓝牙耳机,你不可以在接听电话的时候播放声音文件。

【1】Using MediaPlayer

One of the most important components of the media framework is the MediaPlayer class. An object of this class can fetch, decode, and play both audio and video with minimal setup. It supports several different media sources such as:

  • Local resources
  • Internal URIs, such as one you might obtain from a Content Resolver
  • External URLs (streaming)
使用MediaPlayer类,我们可以播放来自本地的资源,设备内置的资源,外存中的资源(可以是流媒体的格式)

For a list of media formats that Android supports, see the Android Supported Media Formats document.

Here is an example of how to play audio that's available as a local raw resource (saved in your application's res/raw/directory):

下面是一个如何播放AP程序下的资源的例子:

  1. MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);  

  2. mediaPlayer.start(); // no need to call prepare(); create() does that for you  

In this case, a "raw" resource is a file that the system does not try to parse in any particular way. However, the content of this resource should not be raw audio. It should be a properly encoded and formatted media file in one of the supported formats.

放在这个目录下的文件最好不是原始音频文件,可以放置那些可以进行解码,系统支持的文件格式。

And here is how you might play from a URI available locally in the system (that you obtained through a Content Resolver, for instance):

下面是一个播放本地文件的例子:

  1. Uri myUri = ....; // initialize Uri here  

  2. MediaPlayer mediaPlayer = new MediaPlayer(); 
  3.  
  4. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);  

  5. mediaPlayer.setDataSource(getApplicationContext(), myUri);  

  6. mediaPlayer.prepare();  

  7. mediaPlayer.start();  

Playing from a remote URL via HTTP streaming looks like this:

播放一个HTTP协议上的流媒体文件:

  1. String url = "http://........"; // your URL here  

  2. MediaPlayer mediaPlayer = new MediaPlayer();  

  3. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
  4.  
  5. mediaPlayer.setDataSource(url);  

  6. mediaPlayer.prepare(); // might take long! (for buffering, etc)  

  7. mediaPlayer.start();  

Note: If you're passing a URL to stream an online media file, the file must be capable of progressive download.

Caution: You must either catch or pass IllegalArgumentException and IOException when using setDataSource(), because the file you are referencing might not exist.

注意:从流媒体上播放的文件,必须支持在线下载。在使用setDataSource() 的时候要注意捕获 IllegalArgumentException and IOException

【1.1】Asynchronous Preparation

Using MediaPlayer can be straightforward in principle. However, it's important to keep in mind that a few more things are necessary to integrate it correctly with a typical Android application. For example, the call to prepare() can take a long time to execute, because it might involve fetching and decoding media data. So, as is the case with any method that may take long to execute, you should never call it from your application's UI thread. Doing that will cause the UI to hang until the method returns, which is a very bad user experience and can cause an ANR (Application Not Responding) error. Even if you expect your resource to load quickly, remember that anything that takes more than a tenth of a second to respond in the UI will cause a noticeable pause and will give the user the impression that your application is slow.

使用MediaPlayer的时候要注意特殊情况,有些时候在prepare()的时候有可能会花费很长的时间,我们不应该从UI线程中去调用准备音乐,这样有可能引起ANR现象,这样的话会用户体验会很糟糕。

To avoid hanging your UI thread, spawn another thread to prepare the MediaPlayer and notify the main thread when done. However, while you could write the threading logic yourself, this pattern is so common when using MediaPlayer that the framework supplies a convenient way to accomplish this task by using the prepareAsync() method. This method starts preparing the media in the background and returns immediately. When the media is done preparing, the onPrepared() method of the MediaPlayer.OnPreparedListener, configured through setOnPreparedListener() is called.

为了避免这样的情况,我们可以使用另外一个线程来准备好播放然后通知主线程。系统提供了一种方式来处理这样的问题,我们可以使用prepareAsync() 的方法,这个方法会在后台准备好播放的音乐,并立即返回,这个时候会触发 onPrepared() 方法,我们需要通过setOnPreparedListener() 来处理后面需要的操作。

【1.2】Managing State

Another aspect of a MediaPlayer that you should keep in mind is that it's state-based. That is, the MediaPlayer has an internal state that you must always be aware of when writing your code, because certain operations are only valid when then player is in specific states. If you perform an operation while in the wrong state, the system may throw an exception or cause other undesireable behaviors.

另一方面你需要注意的是播放状态。在你编写代码的时候你必须时刻注意MediaPlayer的播放状态,因为当你的播放器处于某些特定的状态时,一些操作将失去效果,否则会出现一些异常的情况。

The documentation in the MediaPlayer class shows a complete state diagram, that clarifies which methods move theMediaPlayer from one state to another. For example, when you create a new MediaPlayer, it is in the Idle state. At that point, you should initialize it by calling setDataSource(), bringing it to the Initialized state. After that, you have to prepare it using either the prepare() or prepareAsync() method. When the MediaPlayer is done preparing, it will then enter the Prepared state, which means you can call start() to make it play the media. At that point, as the diagram illustrates, you can move between theStartedPaused and PlaybackCompleted states by calling such methods as start()pause(), and seekTo(), amongst others. When you call stop(), however, notice that you cannot call start() again until you prepare the MediaPlayer again.

Always keep the state diagram in mind when writing code that interacts with a MediaPlayer object, because calling its methods from the wrong state is a common cause of bugs.

文档中有一个完整的MediaPlayer的播放状态图,它详细的阐述了MediaPlayer状态切换之间的方法。例如…………,你需要牢记那个状态图。如下:



【1.3】Releasing the MediaPlayer

MediaPlayer can consume valuable system resources. Therefore, you should always take extra precautions to make sure you are not hanging on to a MediaPlayer instance longer than necessary. When you are done with it, you should always callrelease() to make sure any system resources allocated to it are properly released. For example, if you are using a MediaPlayerand your activity receives a call to onStop(), you must release the MediaPlayer, because it makes little sense to hold on to it while your activity is not interacting with the user (unless you are playing media in the background, which is discussed in the next section). When your activity is resumed or restarted, of course, you need to create a new MediaPlayer and prepare it again before resuming playback.

简单的理解就是用完的东西当然需要释放,不然还是什么什么不好的后果啦,释放的方法如下:
  1. mediaPlayer.release();  

  2. mediaPlayer = null;  

As an example, consider the problems that could happen if you forgot to release the MediaPlayer when your activity is stopped, but create a new one when the activity starts again. As you may know, when the user changes the screen orientation (or changes the device configuration in another way), the system handles that by restarting the activity (by default), so you might quickly consume all of the system resources as the user rotates the device back and forth between portrait and landscape, because at each orientation change, you create a new MediaPlayer that you never release. (For more information about runtime restarts, see Handling Runtime Changes.)
一个简单的例子来说明释放的重要性,如果你在停止播放的时候没有释放MediaPlayer,却在Activity重新启动的时候又创建了一个MediaPlayer,那么就会发生错误。当屏幕转动的时候,系统会重新创建Activity,那么也会重新创建一个MediaPlayer,这样明显有问题的。

【2】Using a Service with MediaPlayer

If you want your media to play in the background even when your application is not onscreen—that is, you want it to continue playing while the user is interacting with other applications—then you must start a Service and control the MediaPlayerinstance from there. You should be careful about this setup, because the user and the system have expectations about how an application running a background service should interact with the rest of the system. If your application does not fulfil those expectations, the user may have a bad experience. This section describes the main issues that you should be aware of and offers suggestions about how to approach them.

如果你想把音乐在后台播放,那么就需要使用service来控制MediaPlayer的实例。

【2.1】Running asynchronously

First of all, like an Activity, all work in a Service is done in a single thread by default—in fact, if you're running an activity and a service from the same application, they use the same thread (the "main thread") by default. Therefore, services need to process incoming intents quickly and never perform lengthy computations when responding to them. If any heavy work or blocking calls are expected, you must do those tasks asynchronously: either from another thread you implement yourself, or using the framework's many facilities for asynchronous processing.

首先,像一个Activity一样,Service所有的活动都是默认在一个线程里面完成的,实际上,如果你在同一个AP里面运行Activity与Service,他们默认是使用同一个线程的,因此,如果你要同时处理比较繁重的事情,或者说追求更好的运行效果的话,还是最好使用不同的线程或者使用系统框架里面的异步机制。

For instance, when using a MediaPlayer from your main thread, you should call prepareAsync() rather than prepare(), and implement a MediaPlayer.OnPreparedListener in order to be notified when the preparation is complete and you can start playing. For example:

例如在主线程里面使用MediaPlayer的时候的我们应该使用 prepareAsync() rather than prepare() 。

  1. public class MyService extends Service implements MediaPlayer.OnPreparedListener {  

  2.     private static final ACTION_PLAY = "com.example.action.PLAY";  

  3.     MediaPlayer mMediaPlayer = null;  
  4.   
  5.     public int onStartCommand(Intent intent, int flags, int startId) {  

  6.         ...  
  7.         if (intent.getAction().equals(ACTION_PLAY)) {  

  8.             mMediaPlayer = ... // initialize it here  

  9.             mMediaPlayer.setOnPreparedListener(this);  

  10.             mMediaPlayer.prepareAsync(); // prepare async to not block main thread  

  11.         }  
  12.     }  
  13.   
  14.     /** Called when MediaPlayer is ready */  

  15.     public void onPrepared(MediaPlayer player) {  

  16.         player.start();  
  17.     }  
  18. }  


【2.2】Handling asynchronous errors

On synchronous operations, errors would normally be signaled with an exception or an error code, but whenever you use asynchronous resources, you should make sure your application is notified of errors appropriately. In the case of aMediaPlayer, you can accomplish this by implementing a MediaPlayer.OnErrorListener and setting it in your MediaPlayerinstance:

在异步执行的时候,有可能发生一些错误,我们需要捕获那些问题,如下:

  1. public class MyService extends Service implements MediaPlayer.OnErrorListener {  

  2.     MediaPlayer mMediaPlayer;  
  3.   
  4.     public void initMediaPlayer() {  

  5.         // ...initialize the MediaPlayer here...  
  6.   
  7.         mMediaPlayer.setOnErrorListener(this);  
  8.     }  
  9.   
  10.     @Override  
  11.     public boolean onError(MediaPlayer mp, int what, int extra) {  

  12.         // ... react appropriately ...  

  13.         // The MediaPlayer has moved to the Error state, must be reset!  

  14.     }  
  15. }  

【2.3】Using wake locks

When designing applications that play media in the background, the device may go to sleep while your service is running. Because the Android system tries to conserve battery while the device is sleeping, the system tries to shut off any of the phone's features that are not necessary, including the CPU and the WiFi hardware. However, if your service is playing or streaming music, you want to prevent the system from interfering with your playback.

In order to ensure that your service continues to run under those conditions, you have to use "wake locks." A wake lock is a way to signal to the system that your application is using some feature that should stay available even if the phone is idle.

当我们设计一个播放器进行后台播放的时候需要考虑设备进行休眠的情况,因为系统会在一定时候关闭一些不需要的功能,那样可以节省电池,然而,如果你的需要后台播放,那么就需要使得设备在空闲时也处于wake状态。

To ensure that the CPU continues running while your MediaPlayer is playing, call the setWakeMode() method when initializing your MediaPlayer. Once you do, the MediaPlayer holds the specified lock while playing and releases the lock when paused or stopped:

为了保证在播放音乐的时候CPU持续运行,我们需要调用setWakeMode()

  1. mMediaPlayer = new MediaPlayer();  

  2. // ... other initialization here ...  

  3. mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);  

However, the wake lock acquired in this example guarantees only that the CPU remains awake. If you are streaming media over the network and you are using Wi-Fi, you probably want to hold a WifiLock as well, which you must acquire and release manually. So, when you start preparing the MediaPlayer with the remote URL, you should create and acquire the Wi-Fi lock. For example:

然而,如果你使用WIFI来播放流媒体资源,那么还需要保持WIFI的LOCK

  1. WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))  

  2.     .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");  
  3.   
  4. wifiLock.acquire();  

When you pause or stop your media, or when you no longer need the network, you should release the lock:

 当我我们停止播放的时候,需要把WIFI的LOCK释放

wifiLock.release();

【2.4】Running as a foreground service

Services are often used for performing background tasks, such as fetching emails, synchronizing data, downloading content, amongst other possibilities. In these cases, the user is not actively aware of the service's execution, and probably wouldn't even notice if some of these services were interrupted and later restarted.

Service经常用来执行后台任务,例如获取邮件,同步数据,下载内容,在那些情况下,我们很难认识到service的执行状态,也不能发现可能中间被中断过后来重启。

But consider the case of a service that is playing music. Clearly this is a service that the user is actively aware of and the experience would be severely affected by any interruptions. Additionally, it's a service that the user will likely wish to interact with during its execution. In this case, the service should run as a "foreground service." A foreground service holds a higher level of importance within the system—the system will almost never kill the service, because it is of immediate importance to the user. When running in the foreground, the service also must provide a status bar notification to ensure that users are aware of the running service and allow them to open an activity that can interact with the service.

有些时候我们想把一个service在前台执行,这样的话是机会不会被系统给杀死的。当在前台执行的时候,services需要提供一个状态栏的通知来保证用户知道正在执行的service,并且允许用户通过通知栏来打开一个activtiy并且与Service进行交互。

In order to turn your service into a foreground service, you must create a Notification for the status bar and callstartForeground() from the Service. For example:

  1. String songName;  

  2. // assign the song name to songName  

  3. PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,  

  4.                 new Intent(getApplicationContext(), MainActivity.class),  

  5.                 PendingIntent.FLAG_UPDATE_CURRENT);  

  6. Notification notification = new Notification();  

  7. notification.tickerText = text;  

  8. notification.icon = R.drawable.play0;  

  9. notification.flags |= Notification.FLAG_ONGOING_EVENT;  

  10. notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",  

  11.                 "Playing: " + songName, pi);  

  12. startForeground(NOTIFICATION_ID, notification);  
While your service is running in the foreground, the notification you configured is visible in the notification area of the device. If the user selects the notification, the system invokes the PendingIntent you supplied. In the example above, it opens an activity (MainActivity).

当在前台执行的时候,我们可以使用PendingIntent 启动MainActiviy,如下图:

You should only hold on to the "foreground service" status while your service is actually performing something the user is actively aware of. Once that is no longer true, you should release it by calling stopForeground():

stopForeground(true);
我们可以通过上面的方法来停止前台播放效果

【2.5】Handling audio focus

Even though only one activity can run at any given time, Android is a multi-tasking environment. This poses a particular challenge to applications that use audio, because there is only one audio output and there may be several media services competing for its use. Before Android 2.2, there was no built-in mechanism to address this issue, which could in some cases lead to a bad user experience. For example, when a user is listening to music and another application needs to notify the user of something very important, the user might not hear the notification tone due to the loud music. Starting with Android 2.2, the platform offers a way for applications to negotiate their use of the device's audio output. This mechanism is called Audio Focus.

When your application needs to output audio such as music or a notification, you should always request audio focus. Once it has focus, it can use the sound output freely, but it should always listen for focus changes. If it is notified that it has lost the audio focus, it should immediately either kill the audio or lower it to a quiet level (known as "ducking"—there is a flag that indicates which one is appropriate) and only resume loud playback after it receives focus again.

我们需要一种机制来处理Audio Focus的情况,这样可以避免重音的情况。这是必须的,Do you understand?

To request audio focus, you must call requestAudioFocus() from the AudioManager, as the example below demonstrates:

  1. AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);  

  2. int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,  

  3.     AudioManager.AUDIOFOCUS_GAIN);  
  4.   
  5. if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {  

  6.     // could not get audio focus.  
  7. }  
The first parameter to requestAudioFocus() is an AudioManager.OnAudioFocusChangeListener, whoseonAudioFocusChange() method is called whenever there is a change in audio focus. Therefore, you should also implement this interface on your service and activities. For example:
 requestAudioFocus() 的第一个参数是一个 AudioManager.OnAudioFocusChangeListener,当Audio Focus发生变化的时候会触发
onAudioFocusChange() 

  1. class MyService extends Service  

  2.                 implements AudioManager.OnAudioFocusChangeListener {  
  3.     // ....  

  4.     public void onAudioFocusChange(int focusChange) {  

  5.         // Do something based on focus change...  
  6.     }  
  7. }  
The focusChange parameter tells you how the audio focus has changed, and can be one of the following values (they are all constants defined in AudioManager):

FocusChange参数告诉我们Audio focus是如何改变的,下面的几个值:

  • AUDIOFOCUS_GAIN: You have gained the audio focus.
  • AUDIOFOCUS_LOSS: You have lost the audio focus for a presumably long time. You must stop all audio playback. Because you should expect not to have Ffocus back for a long time, this would be a good place to clean up your resources as much as possible. For example, you should release the MediaPlayer.
  • AUDIOFOCUS_LOSS_TRANSIENT: You have temporarily lost audio focus, but should receive it back shortly. You must stop all audio playback, but you can keep your resources because you will probably get focus back shortly.
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: You have temporarily lost audio focus, but you are allowed to continue to play audio quietly (at a low volume) instead of killing audio completely.
  •  下面是一个例子
  1. public void onAudioFocusChange(int focusChange) {  

  2.     switch (focusChange) {  

  3.         case AudioManager.AUDIOFOCUS_GAIN:  

  4.             // resume playback  

  5.             if (mMediaPlayer == null) initMediaPlayer();
  6.   
  7.             else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start(); 
  8.  
  9.             mMediaPlayer.setVolume(1.0f, 1.0f);  

  10.             break;  
  11.   
  12.         case AudioManager.AUDIOFOCUS_LOSS:  

  13.             // Lost focus for an unbounded amount of time: stop playback and release media player
  14.   
  15.             if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();  

  16.             mMediaPlayer.release();  

  17.             mMediaPlayer = null
  18.  
  19.             break;  
  20.   
  21.         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 
  22.  
  23.             // Lost focus for a short time, but we have to stop  

  24.             // playback. We don't release the media player because playback  

  25.             // is likely to resume  

  26.             if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();  

  27.             break;  
  28.   
  29.         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:  

  30.             // Lost focus for a short time, but it's ok to keep playing  

  31.             // at an attenuated level  

  32.             if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);  

  33.             break;  
  34.     }  
  35. }  

Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2) and above, so if you want to support previous versions of Android, you should adopt a backward compatibility strategy that allows you to use this feature if available, and fall back seamlessly if not.

需要记住Audio Focus仅仅在API8以上才提供的

You can achieve backward compatibility either by calling the audio focus methods by reflection or by implementing all the audio focus features in a separate class (say, AudioFocusHelper). Here is an example of such a class:

你可以完成后台兼容通过使用Audio Focus方法或者通过一个现实了audio focus的类。
  1. public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {  

  2.     AudioManager mAudioManager;  
  3.   
  4.     // other fields here, you'll probably hold a reference to an interface  

  5.     // that you can use to communicate the focus changes to your Service  
  6.   
  7.     public AudioFocusHelper(Context ctx, /* other arguments here */) {  

  8.         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);  

  9.         // ...  
  10.     }  
  11.   
  12.     public boolean requestFocus() {  

  13.         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==  

  14.             mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,  

  15.             AudioManager.AUDIOFOCUS_GAIN);  
  16.     }  
  17.   
  18.     public boolean abandonFocus() {  

  19.         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==  

  20.             mAudioManager.abandonAudioFocus(this);  
  21.     }  
  22.   
  23.     @Override  
  24.     public void onAudioFocusChange(int focusChange) {  

  25.         // let your service know about the focus change  
  26.     }  
  27. }  

You can create an instance of AudioFocusHelper class only if you detect that the system is running API level 8 or above. For example:

你可以在检测到系统API>8的时候创建一个AudioFocusHelper class

  1. if (android.os.Build.VERSION.SDK_INT >= 8) {  

  2.     mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
  3.   
  4. else {  

  5.     mAudioFocusHelper = null;  
  6. }  

【2.6】Performing cleanup

As mentioned earlier, a MediaPlayer object can consume a significant amount of system resources, so you should keep it only for as long as you need and call release() when you are done with it. It's important to call this cleanup method explicitly rather than rely on system garbage collection because it might take some time before the garbage collector reclaims theMediaPlayer, as it's only sensitive to memory needs and not to shortage of other media-related resources. So, in the case when you're using a service, you should always override the onDestroy() method to make sure you are releasing theMediaPlayer:

我们之前提过到使用完MediaPlayer之后需要释放资源。我们需要显示的去调用清除的方法而不是依赖系统自动回收机制,因为那样有可能在系统还没有回收的时候你又创建了一个新的实例。所以在我们使用service的时候,我们需要重写 onDestroy() method 来保证释放了MediaPlayer,下面是例子:

  1. public class MyService extends Service {  

  2.    MediaPlayer mMediaPlayer;  
  3.    // ...  
  4.   
  5.    @Override  

  6.    public void onDestroy() {  

  7.        if (mMediaPlayer != null) mMediaPlayer.release();  
  8.    }  
  9. }  

You should always look for other opportunities to release your MediaPlayer as well, apart from releasing it when being shut down. For example, if you expect not to be able to play media for an extended period of time (after losing audio focus, for example), you should definitely release your existing MediaPlayer and create it again later. On the other hand, if you only expect to stop playback for a very short time, you should probably hold on to your MediaPlayer to avoid the overhead of creating and preparing it again.

我们需要尽可能的寻找可以释放MediaPlayer的机会,而不仅仅是关闭的时候。我们可以暂时释放之后又在需要的时候创建,当然当仅仅暂停一会的时候,我们可以持续拥有而不是过度的释放又去创建。

【3】Handling the AUDIO_BECOMING_NOISY Intent

Many well-written applications that play audio automatically stop playback when an event occurs that causes the audio to become noisy (ouput through external speakers). For instance, this might happen when a user is listening to music through headphones and accidentally disconnects the headphones from the device. However, this behavior does not happen automatically. If you don't implement this feature, audio plays out of the device's external speakers, which might not be what the user wants.

许多良好的AP会在发生一些导致音频变得混杂的时候自动停止后台播放的,例如,当用户在用耳机听歌的时候若是发生突然失去连接的情况会产生音频混杂。然而,这个行为不是自动发生的。如果你没有实现这个功能,则不会产生你需要的效果。

You can ensure your app stops playing music in these situations by handling the ACTION_AUDIO_BECOMING_NOISY intent, for which you can register a receiver by adding the following to your manifest:

你可以通过处理 ACTION_AUDIO_BECOMING_NOISY 的Intent来确保你的AP停止播放音乐在那种情况下,那个Intent需要在manifest文件中注册一个receiver。

  1. <receiver android:name=".MusicIntentReceiver">  

  2.    <intent-filter>  

  3.       <action android:name="android.media.AUDIO_BECOMING_NOISY" />  

  4.    </intent-filter>  

  5. </receiver>  


还需要实现这样一个类

  1. public class MusicIntentReceiver implements android.content.BroadcastReceiver {  

  2.    @Override  
  3.    public void onReceive(Context ctx, Intent intent) {  

  4.       if (intent.getAction().equals(  

  5.                     android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {  

  6.           // signal your service to stop playback  

  7.           // (via an Intent, for instance)  
  8.       }  
  9.    }  
  10. }  


【4】Retrieving Media from a Content Resolver

Another feature that may be useful in a media player application is the ability to retrieve music that the user has on the device. You can do that by querying the ContentResolver for external media:
另外一个在MediaPlayer AP里面可能有用的功能是获取用户手机上已经存在的音乐,你可一通过ContentResolver 来查询外部的媒体

  1. ContentResolver contentResolver = getContentResolver();  

  2. Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  

  3. Cursor cursor = contentResolver.query(uri, nullnullnullnull);  

  4. if (cursor == null) {  

  5.     // query failed, handle error.  

  6. else if (!cursor.moveToFirst()) {  

  7.     // no media on the device  

  8. else {  

  9.     int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);  

  10.     int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);  

  11.     do {  

  12.        long thisId = cursor.getLong(idColumn);  

  13.        String thisTitle = cursor.getString(titleColumn); 
  14.  
  15.        // ...process entry...  

  16.     } while (cursor.moveToNext());  

  17. }  

To use this with the MediaPlayer, you can do this:

  1. long id = /* retrieve it from somewhere */;  

  2. Uri contentUri = ContentUris.withAppendedId(  

  3.         android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);  
  4.   
  5. mMediaPlayer = new MediaPlayer();  

  6. mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);  

  7. mMediaPlayer.setDataSource(getApplicationContext(), contentUri);  
  8.   
  9. // ...prepare and start...  

【5】Playing JET content

The Android platform includes a JET engine that lets you add interactive playback of JET audio content in your applications. You can create JET content for interactive playback using the JetCreator authoring application that ships with the SDK. To play and manage JET content from your application, use the JetPlayer class.

Android平台包括一个JET引擎用来让你在AP中为JET音频添加交互式的后台播放。

JET指在嵌入式设备上的音乐播放器,JET engine是控制游戏声音特效的引擎,其使用MIDI格式,并可以控制游戏的时间进度)

For a description of JET concepts and instructions on how to use the JetCreator authoring tool, see the JetCreator User Manual. The tool is available on Windows, OS X, and Linux platforms (Linux does not support auditioning of imported assets like with the Windows and OS X versions).

Here's an example of how to set up JET playback from a .jet file stored on the SD card:

下面是一个如何从存放在SDcard中的.jet文件中创建JET回放的例子:

  1. JetPlayer jetPlayer = JetPlayer.getJetPlayer();  

  2. jetPlayer.loadJetFile("/sdcard/level1.jet");  

  3. byte segmentId = 0;  
  4.   
  5. // queue segment 5, repeat once, use General MIDI, transpose by -1 octave  

  6. jetPlayer.queueJetSegment(5, -11, -10, segmentId++);  

  7. // queue segment 2  

  8. jetPlayer.queueJetSegment(2, -1000, segmentId++);  
  9.   
  10. jetPlayer.play();  


【6】Performing Audio Capture

音频获取比播放要稍微复杂一点,但是也还是比较简单的,如下

Audio capture from the device is a bit more complicated than audio and video playback, but still fairly simple:

  1. Create a new instance of android.media.MediaRecorder.
  2. Set the audio source using MediaRecorder.setAudioSource(). You will probably want to useMediaRecorder.AudioSource.MIC.
  3. Set output file format using MediaRecorder.setOutputFormat().
  4. Set output file name using MediaRecorder.setOutputFile().
  5. Set the audio encoder using MediaRecorder.setAudioEncoder().
  6. Call MediaRecorder.prepare() on the MediaRecorder instance.
  7. To start audio capture, call MediaRecorder.start().
  8. To stop audio capture, call MediaRecorder.stop().
  9. When you are done with the MediaRecorder instance, call MediaRecorder.release() on it. CallingMediaRecorder.release() is always recommended to free the resource immediately.
The example class below illustrates how to set up, start and stop audio capture, and to play the recorded audio file.
  1. /* 
  2.  * The application needs to have the permission to write to external storage 
  3.  * if the output file is written to the external storage, and also the 
  4.  * permission to record audio. These permissions must be set in the 
  5.  * application's AndroidManifest.xml file, with something like: 
  6.  * 
  7.  * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
  8.  * <uses-permission android:name="android.permission.RECORD_AUDIO" /> 
  9.  * 
  10.  */  
  11. package com.android.audiorecordtest;  
  12.   
  13. import android.app.Activity;  
  14. import android.widget.LinearLayout;  
  15. import android.os.Bundle;  
  16. import android.os.Environment;  
  17. import android.view.ViewGroup;  
  18. import android.widget.Button;  
  19. import android.view.View;  
  20. import android.view.View.OnClickListener;  
  21. import android.content.Context;  
  22. import android.util.Log;  
  23. import android.media.MediaRecorder;  
  24. import android.media.MediaPlayer;  
  25.   
  26. import java.io.IOException;  
  27.   
  28.   
  29. public class AudioRecordTest extends Activity  
  30. {  
  31.     private static final String LOG_TAG = "AudioRecordTest";  
  32.     private static String mFileName = null;  
  33.   
  34.     private RecordButton mRecordButton = null;  
  35.     private MediaRecorder mRecorder = null;  
  36.   
  37.     private PlayButton   mPlayButton = null;  
  38.     private MediaPlayer   mPlayer = null;  
  39.   
  40.     private void onRecord(boolean start) {  
  41.         if (start) {  
  42.             startRecording();  
  43.         } else {  
  44.             stopRecording();  
  45.         }  
  46.     }  
  47.   
  48.     private void onPlay(boolean start) {  
  49.         if (start) {  
  50.             startPlaying();  
  51.         } else {  
  52.             stopPlaying();  
  53.         }  
  54.     }  
  55.   
  56.     private void startPlaying() {  
  57.         mPlayer = new MediaPlayer();  
  58.         try {  
  59.             mPlayer.setDataSource(mFileName);  
  60.             mPlayer.prepare();  
  61.             mPlayer.start();  
  62.         } catch (IOException e) {  
  63.             Log.e(LOG_TAG, "prepare() failed");  
  64.         }  
  65.     }  
  66.   
  67.     private void stopPlaying() {  
  68.         mPlayer.release();  
  69.         mPlayer = null;  
  70.     }  
  71.   
  72.     private void startRecording() {  
  73.         mRecorder = new MediaRecorder();  
  74.         mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  75.         mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  
  76.         mRecorder.setOutputFile(mFileName);  
  77.         mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  78.   
  79.         try {  
  80.             mRecorder.prepare();  
  81.         } catch (IOException e) {  
  82.             Log.e(LOG_TAG, "prepare() failed");  
  83.         }  
  84.   
  85.         mRecorder.start();  
  86.     }  
  87.   
  88.     private void stopRecording() {  
  89.         mRecorder.stop();  
  90.         mRecorder.release();  
  91.         mRecorder = null;  
  92.     }  
  93.   
  94.     class RecordButton extends Button {  
  95.         boolean mStartRecording = true;  
  96.   
  97.         OnClickListener clicker = new OnClickListener() {  
  98.             public void onClick(View v) {  
  99.                 onRecord(mStartRecording);  
  100.                 if (mStartRecording) {  
  101.                     setText("Stop recording");  
  102.                 } else {  
  103.                     setText("Start recording");  
  104.                 }  
  105.                 mStartRecording = !mStartRecording;  
  106.             }  
  107.         };  
  108.   
  109.         public RecordButton(Context ctx) {  
  110.             super(ctx);  
  111.             setText("Start recording");  
  112.             setOnClickListener(clicker);  
  113.         }  
  114.     }  
  115.   
  116.     class PlayButton extends Button {  
  117.         boolean mStartPlaying = true;  
  118.   
  119.         OnClickListener clicker = new OnClickListener() {  
  120.             public void onClick(View v) {  
  121.                 onPlay(mStartPlaying);  
  122.                 if (mStartPlaying) {  
  123.                     setText("Stop playing");  
  124.                 } else {  
  125.                     setText("Start playing");  
  126.                 }  
  127.                 mStartPlaying = !mStartPlaying;  
  128.             }  
  129.         };  
  130.   
  131.         public PlayButton(Context ctx) {  
  132.             super(ctx);  
  133.             setText("Start playing");  
  134.             setOnClickListener(clicker);  
  135.         }  
  136.     }  
  137.   
  138.     public AudioRecordTest() {  
  139.         mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();  
  140.         mFileName += "/audiorecordtest.3gp";  
  141.     }  
  142.   

  143.     @Override  
  144.     public void onCreate(Bundle icicle) {  
  145.         super.onCreate(icicle);  
  146.   
  147.         LinearLayout ll = new LinearLayout(this);  
  148.         mRecordButton = new RecordButton(this);  
  149.         ll.addView(mRecordButton,  
  150.             new LinearLayout.LayoutParams(  
  151.                 ViewGroup.LayoutParams.WRAP_CONTENT,  
  152.                 ViewGroup.LayoutParams.WRAP_CONTENT,  
  153.                 0));  
  154.         mPlayButton = new PlayButton(this);  
  155.         ll.addView(mPlayButton,  
  156.             new LinearLayout.LayoutParams(  
  157.                 ViewGroup.LayoutParams.WRAP_CONTENT,  
  158.                 ViewGroup.LayoutParams.WRAP_CONTENT,  
  159.                 0));  
  160.         setContentView(ll);  
  161.     }  
  162.   
  163.     @Override  
  164.     public void onPause() {  
  165.         super.onPause();  
  166.         if (mRecorder != null) {  
  167.             mRecorder.release();  
  168.             mRecorder = null;  
  169.         }  
  170.   
  171.         if (mPlayer != null) {  
  172.             mPlayer.release();  
  173.             mPlayer = null;  
  174.         }  
  175.     }  
  176. }  
原创粉丝点击