MediaPlayer
简介
Android 多媒体框架支持播放各种常见媒体类型,以便轻松地将音频、视频和图片集成到应用中。可以使用 MediaPlayer API,播放存储在应用资源(原始资源)内的媒体文件、文件系统中的独立文件或者通过网络连接获得的数据流中的音频或视频。
概念
MediaPlayer 类是媒体框架最重要的组成部分之一。此类的对象能够获取、解码以及播放音频和视频,而且只需极少量设置。它支持多种不同的媒体源,如本地资源、内部 URI(如从内容解析器获取的 URI)、外部网址(流式传输)。
基础配置
下列示例代码将展示如何播放来自不同资源的音频。
如何播放作为本地原始资源(保存在应用的 res/raw/ 目录中)提供的音频:
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you
在本例中,“原始”资源是指系统不会尝试以任何特定方式解析的文件。不过,该资源的内容不应为原始音频。它应该是采用某种支持的格式且经过适当编码和格式化的媒体文件。
如何播放系统中本地可用的 URI(例如通过内容解析器获取)中的内容:
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
如何通过 HTTP 流式传输并播放远程网址上的内容:
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();
注意: 使用 setDataSource()
时,必须捕获或传递IllegalArgumentException
和 IOException
,因为引用的文件可能并不存在。
基础知识
此处将介绍在案例APP中涉及的如何在Service中使用MediaPlayer。
如果希望即使当应用未在屏幕上显示时,应用仍会在后台播放媒体内容,则必须启动一个 Service 并由此控制 MediaPlayer 实例。需要将 MediaPlayer 嵌入到 MediaBrowserServiceCompat Service 中,并使其在其他Activity 中与 MediaBrowserCompat 进行互动。
异步运行
首先,与 Activity 类似,Service 中的所有工作均默认在单个线程中完成。实际上,如果从同一应用中运行 Activity 和 Service,则它们会默认使用相同的线程(“主线程”)。因此,Service 需要快速处理传入的 Intent,并且在响应它们时避免执行冗长的计算。如果需要完成大量的工作或可能会阻塞调用,则必须异步执行这些任务:从自己实现的其他线程异步执行,或使用框架的诸多工具进行异步处理。
例如,从主线程中使用 MediaPlayer 时,应该调用prepareAsync()
而非 prepare(),并实现 MediaPlayer.OnPreparedListener,以便在准备工作完成后获得通知并开始播放。
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mediaPlayer = ... // initialize it here
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
处理异步错误
在同步操作中,系统通常会通过异常或错误代码来指示错误,但当您使用异步资源时,应确保以适当的方式向应用发出错误通知。对于 MediaPlayer,可以实现 MediaPlayer.OnErrorListener 并在 MediaPlayer 实例中对其进行设置来发出错误通知:
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mediaPlayer;
public void initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer.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!
}
}
注意:出现错误时,MediaPlayer 会进入“Error”状态,必须先进行重置才能再次使用它。
使用唤醒锁定
当设计在后台播放媒体内容的应用时,设备可能会在 Service 运行时进入休眠状态。由于 Android 系统尝试在设备处于休眠状态时节省电量,因此系统会尝试关闭手机上任何不必要的功能,包括 CPU 和 WLAN 硬件。如果 Service 正在播放或流式传输音乐,则需要防止系统干扰播放。
为了确保 Service 在这些情况下能继续运行,必须使用“唤醒锁定”。唤醒锁定可以告诉系统:应用正在使用一些即使在手机处于闲置状态时也应该可用的功能。
为确保 CPU 在 MediaPlayer 播放时继续运行,请在初始化 时调用 setWakeMode()
方法。完成该操作后,MediaPlayer 会在播放时保持指定的锁定状态,并在暂停或停止播放时释放锁定:
mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
上述只能保证CPU保持唤醒状态,如果使用 WLAN 并通过网络流式传输媒体内容也希望保持 WifiLock,则该锁定必须手动获取和释放,代码如下:
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
当暂停或停止媒体内容,或者不再需要网络时,应释放该锁定:
wifiLock.release();
进行清理
MediaPlayer 对象会消耗大量的系统资源,因此应仅使其保留必要的时长,并在操作完成后调用release()
。请勿依赖于系统垃圾回收,因为垃圾回收器要经过一段时间才会回收 MediaPlayer,原因在于它仅对内存需求敏感,而对缺少其他媒体相关资源并不敏感。因此,当使用 Service 时,应始终替换 onDestroy()
方法以确保释放 MediaPlayer:
public class MyService extends Service {
MediaPlayer mediaPlayer;
// ...
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) mediaPlayer.release();
}
}
除了在关闭时释放MediaPlayer 之外,还应始终寻找其他释放机会。例如,如果预计在很长一段时间都无法播放媒体内容(例如,在失去音频焦点后),则应果断释放现有的 MediaPlayer 并在之后重新创建。另一方面,如果预计只是短时间停止播放,则应该保留 MediaPlayer,以避免再次创建和准备它所产生的开销。
参考网站
安卓开发者网站 https://developer.android.google.cn/