学习Android MediaPlayer

Android Media Playback

原文

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.

Android多媒体框架包含对多种常见媒体类型的支持,所以你可以容易的在自己的应用中集成音频,视频和图片。你可以播放应用内的资源文件,或文件系统中独立的文件,也可以通过网络数据流来播放,这些功能都使用MediaPlayer APIs实现。

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.

注意:你只能通过标准输出设备播放音频。当前,这包括移动设备的扬声器或者蓝牙耳机。你不能在用户打电话时播放音频。


The Basics

在Android Framework中,下面两个类用来播放声音和视频:

MediaPlayer  此类是播放声音和视频的主要API。

AudioManager 此类管理音频资源和音频在设备上的输出。


Manifest Declarations

Before starting development on your application using MediaPlayer, make sure your manifest has the appropriate declarations to allow use of related features.

在开始使用MediaPlayer之前,确保你的清单文件中声明了与相关特性有关的权限:

  • Internet Permission - 如果你使用MediaPlayer播放网络内容,应用需要网络访问权限。

  • Wake Lock Permission - 如果你的应用需要保持屏幕不变暗或者处理器不休眠,或者使用MediaPlayer.setScreenOnWhilePlaying()MediaPlayer.setWakeMode()方法,你需要请求以下权限:


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:

媒体框架中最重要的组件之一是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,因为你引用的文件可能不存在。


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.

原则上,使用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方法来简化这一工作。此方法立刻返回,在后台执行准备工作,完成后通过回调通知调用者。


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的另一个关注点是它是基于状态的。也就是说,你写代码时必须始终意识到MediaPlayer有内部状态,因为某些操作只在player处于特定状态时才有效。如果你在错误的状态下执行操作,系统可能会抛出异常或者引发其他不需要的行为。

MediaPlayer类文档中展示了MediaPlayer的完整状态图。

Releasing the 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对象,但从来没有释放。

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 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实例。用户和系统对运行后台服务的应用如何同系统其它部分交互有一些期望,如果你的应用无法满足这些期望,用户可能会有糟糕的体验。本节描述了你需要注意的主要事项以及如何解决的建议。

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中的所有工作也是在主线程中完成的。因此,服务需要快速的处理传入的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();
    }
}

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 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)它。

原文地址:https://www.cnblogs.com/yuanchongjie/p/5089476.html