原文html
The Android multimedia framework includes support for playing 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.react
Android多媒体框架包含对多种常见媒体类型的支持,因此你能够容易的在本身的应用中集成音频,视频和图片。你能够播放应用内的资源文件,或文件系统中独立的文件,也能够经过网络数据流来播放,这些功能都使用MediaPlayer APIs实现。android
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.网络
注意:你只能经过标准输出设备播放音频。当前,这包括移动设备的扬声器或者蓝牙耳机。你不能在用户打电话时播放音频。app
在Android Framework中,下面两个类用来播放声音和视频:框架
MediaPlayer
此类是播放声音和视频的主要API。less
AudioManager
此类管理音频资源和音频在设备上的输出。异步
Before starting development on your application using MediaPlayer, make sure your manifest has the appropriate declarations to allow use of related features.async
在开始使用MediaPlayer以前,确保你的清单文件中声明了与相关特性有关的权限:ide
Internet Permission - 若是你使用MediaPlayer播放网络内容,应用须要网络访问权限。
Wake Lock Permission - 若是你的应用须要保持屏幕不变暗或者处理器不休眠,或者使用MediaPlayer.setScreenOnWhilePlaying()
和MediaPlayer.setWakeMode()
方法,你须要请求如下权限:
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:
媒体框架中最重要的组件之一是MediaPlayer类。该类的对象能够用最少的步骤获取,解码和播放音频和视频。它支持多种媒体源,例如:
本地资源
内部URI,例如用于Content Resolver的URI
外部URL(流)
Android支持的媒体类型,见此文档:Android Supported Media Formats。
下面的例子显示了如何播放本地raw资源(应用res/raw目录下)
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); mediaPlayer.start(); // no need to call prepare(); create() does that for you
本例中,一个“raw”资源是一个系统不会尝试用特殊方式去解析的文件。然而,此资源的内容不能是原始音频。它应该是根据支持的格式恰当编码和格式化的文件。
下面的例子显示如何经过本地URI播放:
Uri myUri = ....; // initialize Uri here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();
下面的例子显示如何经过HTTP访问URL来播放:
String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();
注意:若是你经过URL播放在线媒体文件,该文件必须能够渐近下载(Progressive download)。
警告:在使用setDataSource()
时,必须捕获或者传递IllegalArgumentException和IOException,由于你引用的文件可能不存在。
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.
原则上,使用MediaPlayer是简单直接的。然而集成在Android应用中时,有几点须要注意。例如,调用prepare()
可能会花费很长时间,由于此方法涉及到媒体数据的获取和解码,所以不该该在UI线程调用。
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.
要避免挂起UI线程,使用另外一个线程来准备MediaPlaer,在完成时通知主线程。然而,虽然你能够本身写线程逻辑,此框架提供了prepareAsync
方法来简化这一工做。此方法马上返回,在后台执行准备工做,完成后经过回调通知调用者。
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的另外一个关注点是它是基于状态的。也就是说,你写代码时必须始终意识到MediaPlayer有内部状态,由于某些操做只在player处于特定状态时才有效。若是你在错误的状态下执行操做,系统可能会抛出异常或者引起其余不须要的行为。
MediaPlayer类文档中展现了MediaPlayer的完整状态图。
A 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 call release() to make sure any system resources allocated to it are properly released. For example, if you are using a MediaPlayer and 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.
MediaPlayer会消费宝贵的系统资源,因此必须注意不要保持MediaPlayer实例超过须要的时间。当你用完时,应该调用release()
方法来确保分配给它的系统资源被合适的释放了。例如,若是你正在使用MediaPlayer,而Activity收到了onStop()
回调,则必须释放MediaPlayer(除非你在后台播放)。当Activity 从新得到焦点或者从新开始时,你须要建立一个新的MediaPlayer实例,并在恢复播放前准备它。
将MediaPlayer释放并制空:
mediaPlayer.release(); 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.
例如,若是你在Activity stop时忘了释放MediaPlayer,但在Activity create时建立了新的实例,那在用户反复旋转屏幕时,可能会很快就耗尽全部的系统资源,由于每次方向改变,你都建立了新的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 MediaPlayer instance 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,让后再Service中控制MediaPlayer实例。用户和系统对运行后台服务的应用如何同系统其它部分交互有一些指望,若是你的应用没法知足这些指望,用户可能会有糟糕的体验。本节描述了你须要注意的主要事项以及如何解决的建议。
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中的全部工做也是在主线程中完成的。所以,服务须要快速的处理传入的intent,在响应意图是不能执行长时间操做。若是有大量的工做或者阻塞式的调用,就须要异步的完成。
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()而不是prepare(),实现MediaPlayer.OnPreparedListener
接口来通知准备已经完成。代码以下:
public class MyService extends Service implements MediaPlayer.OnPreparedListener { private static final String ACTION_PLAY = "com.example.action.PLAY"; MediaPlayer mMediaPlayer = null; public int onStartCommand(Intent intent, int flags, int startId) { ... if (intent.getAction().equals(ACTION_PLAY)) { mMediaPlayer = ... // initialize it here mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.prepareAsync(); // prepare async to not block main thread } } /** Called when MediaPlayer is ready */ public void onPrepared(MediaPlayer player) { player.start(); } }
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 a MediaPlayer, you can accomplish this by implementing a MediaPlayer.OnErrorListener and setting it in your MediaPlayer instance:
在同步的调用中,错误一般是经过异常或者错误码表示,但在异步调用时,你应该确保正确的接收到错误。对于MediaPlayer来讲,你能够实现MediaPlayer.OnErrorListener
接口,设置给MediaPlayer实例:
public class MyService extends Service implements MediaPlayer.OnErrorListener { MediaPlayer mMediaPlayer; public void initMediaPlayer() { // ...initialize the MediaPlayer here... mMediaPlayer.setOnErrorListener(this); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { // ... react appropriately ... // The MediaPlayer has moved to the Error state, must be reset! } }
It's important to remember that when an error occurs, the MediaPlayer moves to the Error state (see the documentation for the MediaPlayer class for the full state diagram) and you must reset it before you can use it again.
要记住当错误发生时,MediaPlayer转变为Error状态,在你使用它以前须要重置(reset)它。