关闭

Media Playback//媒体播放

标签: API GuidesMedia and CameraMedia Playback
1063人阅读 评论(0) 收藏 举报
分类:

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.
//安卓的多媒体框架包括支持播放各种常见的媒体类型,所以你可以很容易地整合音频,视频和图片在你的应用里面。你可以播放音频或者视频从储藏在你的应用的资源(raw resources)里的媒体文件,来自与独立文件系统中的文件,或者来自于通过网络连接获取的数据流,都使用MediaPlayer APIS.
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.
//这个文档将向你展示如何写出一个媒体播放的应用,这个应用在用户和系统之间进行交互以获得好的性能和令人愉悦的用户体验。
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//基础知识

The following classes are used to play sound and video in the Android framework:
//下面的这些类在安卓框架里面被用来播放声音和视频:
1)MediaPlayer
This class is the primary API for playing sound and video.
//这个类是主要的,基本的播放声音和视频的API.
2)AudioManager
This class manages audio sources and audio output on a device.
//这个类管理音频源和视频输出到一个设备上。

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 - If you are using MediaPlayer to stream network-based content, your application must request network access.
//网络许可---如果你基于网络内容的流使用MediaPlayer,你的应用必须要求访问网络。
<uses-permission android:name="android.permission.INTERNET" />
Wake Lock Permission- If your player application needs to keep the screen from dimming or the processor from sleeping, or uses the MediaPlayer.setScreenOnWhilePlaying() or MediaPlayer.setWakeMode() methods, you must request this permission.
//锁屏开关的权限---如果你的应用需要禁止屏幕变暗或者处理器休眠,或者需要使用MediaPlayer.setScreenOnWhilePlaying()或者MediaPlayer.setWakeMode()方法,你必须请求这个权限。
<uses-permission android:name="android.permission.WAKE_LOCK" />

Using MediaPlayer//使用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类。这个类的一个对象可以通过最小的设置取得,解码,并且播放音频和视频.它支持几种不同的多媒体源,例如:
1)Local resources//本地资源
2)Internal URIs, such as one you might obtain from a Content Resolver//网络URIs,例如你可以从Content Resolver里面获得一个
3)External URLs (streaming)//外部的URLs(流)
For a list of media formats that Android supports, see the Android Supported Media Formats document.
//android支持的多媒体格式,参见Android Supported Media Formats文档。
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):
//这里是一个例子,这个例子是如何播放本地可用的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//不需要调用prepare(),create()已经为你调用了
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.
//在这种情况下,一个"raw" 资源是一个文件,系统不会以任何特别的方式去解析的文件。但是这个资源的内容不能是一个未加工的音频。它应当被恰当地编码并且是一种被支持的媒体格式。
And here is how you might play from a URI available locally in the system (that you obtained through a Content Resolver, for instance):
//这里是如何让播放存在系统的本地可用的URI(例如,你从一个Content Resolver里面得到)
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
Playing from a remote URL via HTTP streaming looks like this:
//通过一个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();
Note: If you're passing a URL to stream an online media file, the file must be capable of progressive download.
//注意:如果你是通过URL流播放一个在线的媒体文件,这个文件必须有渐进下载的能力。
Caution: You must either catch or pass IllegalArgumentException and IOException when usingsetDataSource(), because the file you are referencing might not exist.
//警告:你必须捕捉或者终止IllegalArgumentException和IOException当使用setDataSource()时,因为你引用的这个文件可能不存在。

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(应用无响应)错误。即使你希望你的自的资源加载的快些,记住在你的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线程停止,生产其它的线程去准备MediaPlayer并且通知主线程什么时间做。然而,当你可以自己写线程的逻辑时,这种模式是很普遍的,使用媒体播放器时,该框架提供了一个方便的方法来完成这项任务通过使用prepareAsync()方法。这个方法在后台开始准备媒体并且立即返回。当媒体已经准备了,MediaPlayer.OnPreparedListener的onPrepared()方法,通过setOnPreparedListener()的配置被调用。

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有一个内部的状态,因为某些操作只有在特别的状态下才是可用的.如果你在一个错误的状态下执行了某个操作,系统会抛出一个异常或者产生一个不令人满意的行为。

The documentation in the MediaPlayer class shows a complete state diagram, that clarifies which methods move the MediaPlayer 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 the StartedPaused 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.
//在MediaPlayer类里面的文档显示了一个完整的状态图解,它澄清了那个方法将MediaPlayer从一个状态移到另一个状态。例如,当你建立一个新的MediaPlayer时,它是处于Idle状态。在那时,你应当调用setDataSource(),把它带到初始化的状态。在那之后,你必须使用prepare()或者prepareAsync()方法准备它。当MediaPlayer正在准备,它将进入PrePared状态,那意味着你可以调用start()让它播放媒体。在那时,就像图解阐明的那样,你可以通过调用方法start(),pause(),seekTo()将它们的状态在Started,Paused,和PlaybackCompleted状态间移动。但是当你调用了stop(),请注意你不能再调用start()方法了,至到你再次准备MediaPlayer.
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对象交互的代码时要将图解的状态记在心里,因为在一个错误的状态调用它的方法是一个错误的常见的原因。

Releasing the MediaPlayer//释放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 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 theMediaPlayer, 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的实例。当你使用(MediaPlayer)完成时,你应当调用release()方法确信与它相关的任何系统资源都被适当的释放。例如,如果你在使用一个MediaPlayer并且你的activity接收到了onStop()的调用,你必须释放MediaPlayer,因为当你的activity不再与用户交互时就没有必要再抓住它(MediaPlayer)了(除非你在后台播放media,那将在下一部分讨论).因此,当你的activity恢复或者重新开始时,你需要在恢复播放前建立一个新的MediaPlayer并且准备它。
Here's how you should release and then nullify your 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 newMediaPlayer that you never release. (For more information about runtime restarts, see Handling Runtime Changes.)
//作为一个例子,考虑可能发生的这样的问题,当你的activity已经停止了,但你忘记了释放MediaPlayer,当这个Activity再一次开始时会创建一个新的MediaPlayer.正如你可能知道的,当用户改变屏幕的方向(或者以其它方法改变设备的配置),系统的处理是重新开始activity(默认),所以当用户在竖屏和横屏之间向前和向后旋转设备时,你可能会很快地消耗系统所有的资源。因为任何一次方向的改变,你都建立了一个你从没有释放的MediaPlayer.(更多的关于运行时重新启动的信息,参见Handling Runtime Changes.)
You may be wondering what happens if you want to continue playing "background media" even when the user leaves your activity, much in the same way that the built-in Music application behaves. In this case, what you need is a MediaPlayer controlled by a Service, as discussed in Using a Service with MediaPlayer.
//你可能想知道,当用户已经离开了你的activity,而你想继续播放"后台的media"会发生什么,就像系统内置的音乐应用所表现的行为那样。在这种情况下,你需要的是通过一个服务控制MediaPlayer,就像在Using a Service with MediaPlayer讨论的那样。

Using a Service with MediaPlayer//和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并且控制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里面的所有工作默认的都在一个单独的线程里面完成----事实上,如果你想在同样一个应用里面运行一个activity和一个service,它们默认使用同样的线程("主线程").因此,services需要快速地处理传进来的意图并且当相应它们时从来不执行长时间的计算。如果预期有任何沉重的工作后阻塞的话,你必须异步地处理这些任务:你自己实现的另外的线程,或者使用框架的许多为异步处理的工具。
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 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移动到错误的状态(参见MediaPlayer类的文档里为所有状态的图解)并且当你再次使用它时你必须重置它。

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.
//当设计一个在后台播放媒体的应用时,当你的service在运行时设备可能会休眠。因为安卓系统在试图保护电池,当设备休眠时,系统试图关闭那些不是必须的电话的功能,包括CPU和WiFi硬件。但是,如果你的服务在播放或者流动音乐,你想阻止系统打断你的播放。
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 locks".一个wake lock是一个方法----发信号告诉系统你的应用正在使用的功能应当保持在可用的状态即使电脑在闲置的状态下。
Notice: You should always use wake locks sparingly and hold them only for as long as truly necessary, because they significantly reduce the battery life of the device.
//注意:你应当少使用wake lock并且只在真正必需的时候保持它们,因为它们会显著地减少设备电池的寿命。
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在你的MediaPlay播放的时候能够继续工作,在初始化的时候调用setWakeMode()方法.一旦你这样做,MediaPlayer在播放时持有特定的锁并且在暂停或者停止时释放锁:
mMediaPlayer = new MediaPlayer();
// ... other initialization here ...
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:
//然而,在这个例子里面获得的唤醒锁仅仅保持CPU在醒着的状态。如果你通过网络(使用)流媒体并且使用Wi-Fi,你可能也需要持有WifiLock,WifiLoak你必须手动地获取和释放。所以,当你通过远程的URL开始准备MediaPlayer时,你应当建立和获取Wi-Fi锁。例如:
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();
When you pause or stop your media, or when you no longer need the network, you should release the lock:
//当你暂停或者停止你的media时,或者你不需要网络时,你应当释放锁:
wifiLock.release();

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.
//服务经常被用来执行后台任务,例如,获取emails,同步数据,下载内容,其它可能的事情。在这些情况下,用户没有主动地意识到服务的运行,并且可能没有注意到,如果这些服务被打断并且在稍后重新开始。
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.
//但是考虑正在播放音乐的一个服务的情况。很明显这是一个服务并且是用户主动意识到的,任何的打断都可能严重地影响用户的体验。此外,它是一个服务---用户希望在它执行期间可以与它交互。在这种情况下,服务应当作为一个"前台服务"运行。一个前台的服务在系统内有一个高水平的重要性--
系统几乎从不会杀死服务,因为对用户它是直接重要的.当在前台运行时,服务必须提供一个状态栏通知确保用户知道这个正在运行的服务并且允许他们打开一个可以和服务交互的一个activity.
In order to turn your service into a foreground service, you must create a Notification for the status bar and call startForeground() from the Service. For example:
//为了把你的服务变成一个前台服务,你必须为状态栏建立一个通知并且从这个Service调用startForeground(),例如:
String songName;
// assign the song name to songName
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification();
notification.tickerText = text;
notification.icon = R.drawable.play0;
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",
                "Playing: " + songName, pi);
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.在上面的例子中,它打开了一个activity(MainActivity).
Figure 1 shows how your notification appears to the user:
//图一显示了你的通知如何呈现给用户

Figure 1. 
Screenshots of a foreground service's notification, showing the notification icon in the status bar (left) and the expanded view (right).
//前台服务通知的截屏,显示了通知的图标在状态栏(左面)和展开的视图(right).
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()释放它。
stopForeground(true);
For more information, see the documentation about Services and Status Bar Notifications.
//有关更多信息,请参见关于Services和Status Bar Notification的文档。

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.
//虽然仅有的一个activity可以在给定的任何时间内运行,安卓是一个多任务的环境。这对使用音频的应用构成了特别的挑战,因为仅有一个音频输出但是可能有几个互相竞争的使用者。在安卓2.2之前,没有针对这个问题的内置的机制,在某些情况下它导致了很差的用户体验。例如,当一个用户正在听音乐,其它的应用需要通知用户一些非常重要的事,因为大声的音乐用户可能没有听到通知的声音。从安卓2.2开始,平台提供了一种方法越过设备音频输出的使用。这个机制是叫做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 is cooperative in nature. That is, applications are expected (and highly encouraged) to comply with the audio focus guidelines, but the rules are not enforced by the system. If an application wants to play loud music even after losing audio focus, nothing in the system will prevent that. However, the user is more likely to have a bad experience and will be more likely to uninstall the misbehaving application.
//音频焦点在本质上是合作的.就是,应用是被期望(和非常鼓励)遵守音频焦点指导方针,但是这个规则不是被系统强制执行的.如果一个应用想要在失去焦点后仍要大声地播放音乐,系统没有什么可以阻止它。但是,用户可能有一个糟糕的奖励,并且更可能卸载到这个行为不当的应用。
To request audio focus, you must call requestAudioFocus() from the AudioManager, as the example below demonstrates:
//请求音频的焦点,你必须从AudioManager()里面调用requestAudio()方法,像下面的示范例子一样:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
    AudioManager.AUDIOFOCUS_GAIN);

if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // could not get audio focus.
}
The first parameter to requestAudioFocus() is an AudioManager.OnAudioFocusChangeListener, whose onAudioFocusChange() 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,它的onAudioFocusChange()方法在音频的焦点被改变时调用.因此,你应当在你的service和activities里面实现这个接口。例如:
class MyService extends Service
                implements AudioManager.OnAudioFocusChangeListener {
    // ....
    public void onAudioFocusChange(int focusChange) {
        // Do something based on focus change...
    }
}
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告诉你音频的焦点是如何发生改变的,它的值是下面这些值中的一个(他们都在AudioManager里面被定义了):
1)AUDIOFOCUS_GAIN: You have gained the audio focus.//你已经得到了音频的焦点
2)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 focus 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.
//你已经失去了音频焦点大概很长时间了。你必须停止音频的播放。因为你应该期待你没有长时间的有焦点,这是你尽可能地清理你的资源的好地方。例如,你应当释放MediaPlayer.
3)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.    transient:短暂的
//你暂时失去了音频焦点,但是应当很快收回来。你应当停止所有音频的播放,但是你应当保持你的资源,因为你可能不久获得焦点。
4)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.
//你已经暂时地失去了音频的焦点,但是你被允许继续低声(以一个小的音量值)播放而不是完全杀死音频。
Here is an example implementation:
//这里是一个实现的例子:
public void onAudioFocusChange(int focusChange) {
    switch (focusChange) {
        case AudioManager.AUDIOFOCUS_GAIN:
            // resume playback
            if (mMediaPlayer == null) initMediaPlayer();
            else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
            mMediaPlayer.setVolume(1.0f, 1.0f);
            break;

        case AudioManager.AUDIOFOCUS_LOSS:
            // Lost focus for an unbounded amount of time: stop playback and release media player
            if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
            break;

        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            // Lost focus for a short time, but we have to stop
            // playback. We don't release the media player because playback
            // is likely to resume
            if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
            break;

        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // Lost focus for a short time, but it's ok to keep playing
            // at an attenuated level
            if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
            break;
    }
}
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.
//记住音频焦点的APIs只在API 8(安卓2.2)以及更高的版本里可用,所以如果你想支持安卓以前的版本,你应该采取向后兼容的策略,如果可用的话就使用这一功能,如果不可以就无缝地后退。
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的方法,或者在一个分离的类(叫做,AudioFocusHelper)里面实现所有的音频焦点功能的方法获得向后的兼容性。这里是这样的一个类的例子:
public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
    AudioManager mAudioManager;

    // other fields here, you'll probably hold a reference to an interface
    // that you can use to communicate the focus changes to your Service

    public AudioFocusHelper(Context ctx, /* other arguments here */) {
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        // ...
    }

    public boolean requestFocus() {
        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
            mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN);
    }

    public boolean abandonFocus() {
        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
            mAudioManager.abandonAudioFocus(this);
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        // let your service know about the focus change
    }
}
You can create an instance of AudioFocusHelper class only if you detect that the system is running API level 8 or above. For example:
//你可以建立一个AudioFocusHelper类的实例如果你发觉你的系统运行在API 8或者以上时。例如:
if (android.os.Build.VERSION.SDK_INT >= 8) {
    mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
} else {
    mAudioFocusHelper = null;
}

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 the MediaPlayer, 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 the MediaPlayer:
//像前面提到的那样,一个MediaPlayer对象会消耗大量的系统资源,所以你应当仅仅在你需要的时候保持它,并且在你使用完它之后调用release()方法。
















0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:192199次
    • 积分:3381
    • 等级:
    • 排名:第10272名
    • 原创:101篇
    • 转载:16篇
    • 译文:39篇
    • 评论:11条
    文章分类
    最新评论